<?xml version="1.0" encoding="windows-1251"?>
<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink">
 <stylesheet type="text/css">
        section section title { page-break-before: auto; page-break-after: avoid }
  p &gt; code { font-size: 94% }
  cite { font-style: normal; }
  subtitle { font-style: normal; }
 </stylesheet>
 <description>
  <title-info>
   <genre>comp_osnet</genre>
   <author>
    <first-name>Брайан</first-name>
    <middle-name>Уилсон</middle-name>
    <last-name>Керниган</last-name>
   </author>
   <author>
    <first-name>Роб</first-name>
    <last-name>Пайк</last-name>
   </author>
   <book-title>UNIX — универсальная среда программирования</book-title>
   <annotation>
    <p>В книге американских авторов — разработчиков операционной системы UNIX — блестяще решена проблема автоматизации деятельности программиста, системной поддержки его творчества, выходящей за рамки языков программирования. Профессионалам открыт богатый "встроенный" арсенал системы UNIX. Многочисленными примерами иллюстрировано использование языка управления заданиями <code>shell</code>.</p>
    <p>Для программистов-пользователей операционной системы UNIX.</p>
   </annotation>
   <date>1992</date>
   <coverpage>
    <image l:href="#img_0.jpeg"/></coverpage>
   <lang>ru</lang>
   <src-lang>en</src-lang>
   <translator>
    <first-name>А.</first-name>
    <middle-name>М.</middle-name>
    <last-name>Березко</last-name>
   </translator>
   <translator>
    <first-name>В.</first-name>
    <middle-name>А.</middle-name>
    <last-name>Иващенко</last-name>
   </translator>
  </title-info>
  <src-title-info>
   <genre>comp_osnet</genre>
   <author>
    <first-name>Brian</first-name>
    <middle-name>W.</middle-name>
    <last-name>Kernighan</last-name>
   </author>
   <author>
    <first-name>Rob</first-name>
    <last-name>Pike</last-name>
   </author>
   <book-title>The UNIX Programming Environment</book-title>
   <date>1984</date>
   <lang>en</lang>
  </src-title-info>
  <document-info>
   <author>
    <nickname>honorato bonafe</nickname>
   </author>
   <program-used>OOoFBTools-2.5 (ExportToFB21), FictionBook Editor Release 2.6.6</program-used>
   <date value="2015-04-18">18.04.2015</date>
   <id>OOoFBTools-2015-3-8-16-16-9-1140</id>
   <version>1.0</version>
   <history>
    <p>1.0 pdf-&gt;fb2</p>
   </history>
  </document-info>
  <publish-info>
   <book-name>UNIX — универсальная среда программирования</book-name>
   <publisher>Финансы и статистика</publisher>
   <city>Москва</city>
   <year>1992</year>
   <isbn>5-289-00253-4</isbn>
  </publish-info>
 </description>
 <body>
  <title>
   <p>Керниган Б. В., Пайк Р.</p>
   <p>UNIX — универсальная среда программирования</p>
  </title>
  <section>
   <title>
    <p>Предисловия</p>
   </title>
   <section>
    <title>
     <p>Предисловие к русскому изданию</p>
    </title>
    <p>Операционная система UNIX пользуется в нашей стране заслуженной известностью. Начиная с 1985 г. вышло порядка 10 книг, рассказывающих об этой системе или ее отечественных аналогах. Специалисты в области вычислительной техники и программисты в других отраслях давно уже применяют систему в своей профессиональной деятельности. Развиваются советские операционные системы ДЕМОС, ИНМОС, МОС и другие, представляющие собой различные вариации на тему UNIX.</p>
    <p>Жизнь UNIX оказалась большой и насыщенной событиями. Появившись 20 лет тому назад как инструментальная среда для профессиональных программистов, система благодаря своему изяществу быстро приобрела поклонников и стала использоваться широко. Одно из основных свойств UNIX — мобильность — позволило переносить ее на компьютеры с различной архитектурой, что существенно повысило интерес к ней. Нельзя сказать, что шествие UNIX было триумфальным, поскольку она оказалась не свободной от недостатков (слабость межпроцессного взаимодействия, низкая надежность, недружественность человеко-машинного интерфейса, неразвитость прикладного багажа и др.) и обладала не слишком выдающимися техническими характеристиками, но всем этим пришлось поступиться ради концептуального изящества, универсальности и мобильности. Хотя система и была реализована на большинстве 16-разрядных компьютеров, коммерческого распространения она там не получила и, как правило, побивалась любой операционной системой, создававшейся специально для данной архитектуры. Постановка на 16-разрядные компьютеры скорее была данью моде и рекламе, чем серьезной коммерции.</p>
    <p>Начавшаяся эпоха 32-разрядных микропроцессоров постепенно подтверждает прогнозы о том, что родным для UNIX станет именно этот класс компьютеров. Неважные технические характеристики системы компенсируются высокими техническими параметрами компьютера, а мобильность делает ее перенос более предпочтительным, чем разработка новой операционной системы для 32-разрядной архитектуры. В еще большей степени система оказалась пригодной для активно развиваемых в последнее время компьютеров с RISC-архитектурой.</p>
    <p>Утвердившись на 32-разрядных компьютерах, UNIX, по-видимому, вступила в период зрелости. "Изюминки" системы стали классикой системного программирования. Ведущие компьютерные компании, имеющие устойчивую репутацию производителей собственных операционных систем, делают серьезную ставку на UNIX для своих 32-разрядных компьютеров. Ассоциации UNIX International и Open Software Foundation под эгидой соответствующих фирм (AT&amp;T и IBM) усиливают борьбу за лидерство в сфере UNIX. Рабочие группы института IEEE под эгидой международной организации стандартов ISO разрабатывают стандарты на интерфейсы мобильной операционной среды, беря за основу интерфейсы UNIX.</p>
    <p>Книга Б. В. Кернигана и Р. Пайка — это не просто еще одно издание по модной системе. Написанная превосходными специалистами, один из которых (Б. В. Керниган) стоял у истоков UNIX и языка Си, книга заметно выделяется в общем потоке литературы по UNIX, в том числе и переведенной у нас. При сравнительно небольшом объеме рассматриваются не только общие вопросы строения, функционирования и использования системы, программирования основных операций, но и инструментарий обработки текстов, а также средства создания программ. Соотношение глубины изложения и широты охвата материала выбрано близким к оптимальному, и у читателя по прочтении очередной главы не возникает ощущения, что он либо получил весьма поверхностные сведения, либо, наоборот, чрезмерно углубился в нечто не столь существенное.</p>
    <p>Книгу могут изучать начинающие, не знакомые с системой программисты. Таким читателям адресована первая глава, в общих чертах описывающая круг основополагающих идей UNIX. Затем последовательно рассматриваются свойства системы на разных уровнях интерфейса пользователя (программы), начиная от командного языка и кончая примитивами нижнего уровня (и даже внутренними структурами данных системы). Изложение сопровождается большим количеством примеров, иллюстрирующих как вопросы частного характера, так и технологию решения типовых задач, в частности создание программы с помощью инструментальных средств построения программ. Подробно разбираются файловая система, командный интерпретатор <code>shell</code>, методология программирования на командном языке и применения фильтров, стандартные средства буферизованного ввода-вывода и ввод-вывод нижнего уровня, генераторы лексических и синтаксических анализаторов и построитель программ <code>make</code>, форматирование текстов. В результате читатель получает цельное представление о возможностях системы и методах их использования для решения практических задач программирования.</p>
    <p>Несомненно, педагогическое мастерство авторов и хорошо продуманные примеры позволяют рекомендовать эту книгу всем, кто хочет основательно изучить одну из самых знаменитых операционных систем нашего времени.</p>
    <cite>
     <text-author>М. И. Беляков</text-author>
    </cite>
   </section>
   <section>
    <title>
     <p>Предисловие</p>
    </title>
    <epigraph>
     <p>Число работающих систем UNIX достигло уже 10, и в дальнейшем возрастет еще больше.</p>
     <text-author>(Справочное руководство по системе UNIX, 2-е изд., июнь, 1972)</text-author>
    </epigraph>
    <p>Операционная система UNIX начала свой жизненный путь на "заброшенной" машине DEC PDP-7 BELL Laboratories в 1969 г.<a l:href="#n1" type="note">[1]</a> К. Томпсон при поддержке Р. Канадея, Д. МакИлроя, Д. Осанна и Д. Ритчи написал небольшую систему разделения времени общего назначения, оказавшуюся достаточно удобной, что привлекло энтузиастов-пользователей и послужило основанием для приобретения более мощной машины — PDP-11/20. Одним из первых пользователей системы считается Д. Ритчи, который помог в 1970 г. перенести ее на PDP-11. Д. Ритчи также разработал и написал компилятор с языка программирования Си. В 1973 г. Д. Ритчи и К. Томпсон переписали ядро системы UNIX на языке Си, отойдя от традиции создания таких программ на языке Ассемблера. В этом последнем варианте система в основном сохранилась и по сей день.</p>
    <p>В 1974 г. UNIX была передана университетам "для образовательных целей", а несколько лет спустя нашла и коммерческое применение. В течение всего этого периода система UNIX продолжала совершенствоваться в BELL Laboratories, получив признание в лабораториях, при создании программного обеспечения, в центрах обработки информации, при поддержке операций телефонных компаний. В настоящее время диапазон ее использования весьма широк — от микрокомпьютеров до самых больших универсальных машин.</p>
    <p>В чем причины успеха системы UNIX? Их несколько. Во-первых, поскольку UNIX написана на языке Си, она переносима и, как уже отмечалось, имеет широкий диапазон применения. Во-вторых, доступны исходные тексты программ системы, написанные на языке высокого уровня, что позволяет легко адаптировать ее в соответствии с конкретными требованиями. Наконец, самое главное — это хорошая операционная система, особенно для программистов. Программная среда UNIX необычайно богата и результативна.</p>
    <p>Вместе с UNIX появилось много новых программ и методов программирования, но никакая отдельная программа или идея не дает гарантии в отношении качества системы. Эффективность системы достигается благодаря определенному подходу к программированию, своего рода философии использования вычислительной машины. Основной смысл ее состоит в том, что мощь системы обусловливается взаимодействием программ, а не мощью самих программ. Многие программы UNIX могут решать простейшие задачи, но при объединении с другими программами они превращаются в универсальные и полезные средства. Поэтому в своей книге мы уделяем большое внимание вопросам, связанным с взаимодействием программ и с их использованием в качестве инструментария для создания других программ. Чтобы успешно работать с UNIX, необходимо не только знать, как применять ту или иную программу, но и понимать ее роль в системе.</p>
    <p>По мере распространения UNIX число специалистов, хорошо владеющих системой, к сожалению, уменьшается. Помочь здесь может только время (когда-то и опытные пользователи, включая авторов, могли найти лишь "неуклюжее" решение задачи или написать программы для выполнения задач, с которыми вполне можно справиться уже существующими средствами). Конечно, найти элегантные решения без определенного опыта и знаний нелегко. Мы надеемся, что, изучив материал книги, вы достигнете такой степени компетентности, когда работа в системе, независимо от того, новичок вы или профессионал, становится эффективной и доставляет удовольствие.</p>
    <p>Если наша книга поможет программистам-индивидуалам сделать свой труд более производительным, то тем самым повысится производительность и больших коллективов. Хотя книга в основном рассчитана на программистов, для понимания первых четырех или даже пяти глав программистский опыт не требуется, так что они могут оказаться полезными всем без исключения. Всюду, где было возможно, в качестве примеров мы приводили настоящие программы. Многие из них уже вошли в наш повседневный программный обиход. Все примеры проверялись на машине прямо из текста, который представлен в форме, пригодной для ввода.</p>
    <p>Книга построена следующим образом. В гл. 1 излагаются основные возможности UNIX (вход в систему, почта, система файлов), рассматриваются наиболее употребительные команды и даются начальные сведения об интерпретаторе команд. Опытные пользователи могут опустить эту главу.</p>
    <p>В гл. 2 обсуждается файловая система UNIX. Файловая система является центральным звеном в обеспечении успешной работы, и ее нужно хорошо понимать. Здесь описываются файлы и каталоги, права доступа и режимы файлов, а также индексные дескрипторы. Приводятся краткое описание иерархии файловой системы и пояснения относительно файлов устройств.</p>
    <p>Интерпретатору команд (<code>shell</code>), этому основополагающему средству не только для выполнения программ, но и для их написания, посвящена гл. 3. Вы узнаете, как использовать <code>shell</code> для своих целей (новые команды, аргументы команд, переменные <code>shell</code>, простые структуры управления и переключение ввода-вывода).</p>
    <p>Глава 4 познакомит вас с фильтрами, т.е. программами, которые производят какие-то простые преобразования данных по мере их получения. В первой части главы рассматривается команда контекстного поиска <code>grep</code> и родственные ей команды. Затем обсуждаются более общие фильтры, такие, как <code>sort</code>. В заключение описываются две программы преобразования данных общего назначения <code>sed</code> и <code>awk</code>. Потоковый редактор sed редактирует поток данных по мере их получения, <code>awk</code> — язык программирования для простых операций поиска информации и задач генерации отчетов. Эти программы, иногда в сочетании с интерпретатором shell, позволяют в ряде ситуаций обойтись без традиционных языков программирования.</p>
    <p>В гл. 5 обсуждается, как с помощью языка <code>shell</code> можно создавать программы, которые будут использоваться другими людьми. Тема включает более развитые структуры управления и переменные, перехват и обработку прерываний. Приводятся примеры с привлечением программ <code>sed</code> и <code>awk</code>, а также интерпретатора <code>shell</code>.</p>
    <p>О том, как написать новые программы, используя стандартную библиотеку ввода-вывода, рассказывается в гл. 6. Программы написаны на языке Си, который предполагается известным, или по крайней мере должен быть изучен параллельно. Мы пытаемся показать разумную стратегию проектирования и организации новых программ: как создавать их путем осмысленных приближений и как применять уже существующие средства.</p>
    <p>Далее (гл. 7) мы переходим к системным обращениям, являющимся фундаментом для всех остальных слоев программного обеспечения. Здесь обсуждаются вопросы ввода-вывода, создания файла, обработки ошибки, описываются каталоги, индексные дескрипторы, процессы и сигналы.</p>
    <p>В гл. 8 рассматриваются развитые программные средства: программа <code>yacc</code>, создающая программы грамматического разбора; <code>make</code>, управляющая процессом трансляции больших программ; <code>lex</code>, создающая лексические анализаторы. Изложение строится на примере создания большой программы — программного калькулятора в стиле языка Си.</p>
    <p>В гл. 9 вашему вниманию предлагаются средства подготовки документации, которые сопровождаются описаниями на уровне пользователя и страницей руководства по калькулятору из гл. 8. Данную главу можно читать независимо от остальных глав.</p>
    <p>В конце книги приводятся приложения. В приложении 1 суммируются возможности стандартного редактора <code>ed</code>. Хотя многие читатели предпочтут для повседневного пользования иной редактор, <code>ed</code> представляется общедоступным, действенным и эффективным средством. Регулярные выражения редактора являются ключевым понятием других программ, таких, как <code>grep</code> и <code>sed</code>, и даже только по этой причине его стоит изучить. Приложение 2 содержит справочное руководство по языку калькулятора из гл. 8, а приложение 3 — распечатку окончательной версии программы калькулятора, где для удобства чтения собраны воедино все программные фрагменты.</p>
    <p>Некоторые практические вопросы. Во-первых, система UNIX стала очень популярной, и несколько ее версий нашли широкое применение. Например, седьмая версия происходит от первоначальной системы UNIX вычислительного и научно-исследовательского центра фирмы BELL. System III и System V — версии, официально поддерживаемые фирмой. Университет в Беркли (штат Калифорния) распространяет системы UCB 4.xBSD, производные от шестой версии. Помимо названных существуют многочисленные версии, особенно на малых машинах, которые созданы на базе седьмой версии. Мы пытались справиться с этим многообразием, стараясь максимально придерживаться каких-то общих концепций, одинаковых для всех систем. Для большей точности мы выбрали представление, базирующееся на седьмой версии, поскольку она является основой почти всех широко используемых систем UNIX. Программы прогонялись на System V фирмы BELL и на 4.1BSD из Беркли, причем потребовались лишь тривиальные изменения в нескольких примерах. Независимо от вашей версии системы найденные вами различия будут минимальны.</p>
    <p>Далее, несмотря на обширность материала книги, есть вопросы, которых мы не касались. Основным источником информации для вас является справочное руководство по системе UNIX. Вы можете обращаться к нему, чтобы разрешить свои сомнения или определить, насколько ваша система отличается от нашей.</p>
    <p>Наконец, мы убеждены в том, что лучший способ изучения чего-либо — это эксперимент. Книгу следует читать за терминалом, находя подтверждение либо опровергая прочитанное, исследуя ограничения и варианты. Читайте, проверяйте и снова читайте. Система UNIX — хотя и не идеальный, но удивительный программный мир, и мы надеемся, что вы откроете его для себя.</p>
    <p>Всем, кто так или иначе помогал нам в процессе работы над книгой, мы выражаем искреннюю признательность.</p>
    <cite>
     <text-author>Б. В. Керниган,</text-author>
     <text-author>Р. Пайк</text-author>
    </cite>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 1</p>
    <p>UNIX для начинающих</p>
   </title>
   <section>
    <p>Что такое UNIX? В узком смысле слова это ядро операционной системы разделения времени, т.е. программа, которая распоряжается ресурсами вычислительной машины и предоставляет их пользователям. Она дает пользователям возможность запускать свои программы, управляет периферийными устройствами машины (дисками, терминалами, печатающими устройствами и т.п.) и обеспечивает работу файловой системы, предназначенной для длительного хранения информации, в частности программ, данных или документов.</p>
    <p>В более широком смысле под UNIX часто понимают не только ядро, но и такие важные компоненты, как компиляторы, редакторы, программы копирования и печати файлов. Сюда даже относят языки управления заданиями (командные языки). UNIX может также включать созданные вами или другими пользователями программы, работающие в вашей системе и предназначенные для подготовки документации, статистического анализа, или, скажем, "графические" средства. Значение слова "UNIX" в каждом конкретном случае зависит от того, на каком уровне вы рассматриваете систему. В дальнейшем изложении смысл этого термина вам будет ясен из контекста.</p>
    <p>Иногда система UNIX кажется более сложной, чем она есть на самом деле. Новичку нелегко понять, как наилучшим образом воспользоваться доступными средствами. Но, к счастью, начинать работать в системе не так уж и трудно, а познакомившись с несколькими программами, вы получите достаточное стартовое ускорение. Цель настоящей главы помочь вам по возможности скорее освоить систему. Это не справочное руководство, а лишь краткий обзор приводимого в книге материала, причем многие вопросы будут рассмотрены более детально в последующих главах. Мы обсудим здесь следующие темы:</p>
    <p>• основные понятия: вход и выход из системы, простые команды, исправление ошибок при вводе с терминала, связь между терминалами;</p>
    <p>• повседневная работа: файлы и файловая система, печать файлов, каталоги, часто используемые команды;</p>
    <p>• интерпретатор команд <code>shell</code>: сокращенная форма имен файлов, переключение ввода-вывода, программные каналы, установка символов удаления и стирания, задание вашего собственного пути поиска команд.</p>
    <p>Если вы ранее работали в системе UNIX и большая часть материала главы покажется вам знакомой, то можете перейти сразу к гл. II. Но, даже прочитав эту главу, вы будете вынуждены обращаться к справочному руководству по системе, поскольку данная книги не заменяет его, а лишь дает рекомендации по применению описанных в нем команд. Более того, вы можете обнаружить расхождение в тексте книги и описании вашей системы. В начале справочного руководства приводится предметный указатель (индекс), который поможет вам ориентироваться в программах. Научитесь им пользоваться.</p>
    <p>В заключение хотелось бы дать вам совет: не бойтесь экспериментировать. Если вы новичок, то существует очень мало ситуаций, в которых вы можете повредить себе или другим пользователям, так что изучайте свою систему методом "проб и ошибок". Первая глава довольно большая, и лучший способ ознакомиться с ней читать за один прием всего несколько страниц, постоянно экспериментируя.</p>
   </section>
   <section>
    <title>
     <p>1.1 Итак, приступаем</p>
    </title>
    <subtitle>Некоторые предварительные замечания о терминалах и вводе символов</subtitle>
    <p>Мы не собираемся здесь затрагивать вопросы, связанные с применением вычислительных машин, поскольку предполагается, что вы имеете представление о терминалах и о том, как ими пользоваться. Если по ходу изложения вам что-нибудь покажется непонятным, обратитесь за помощью к местному специалисту.</p>
    <p>Система UNIX полностью <emphasis>дуплексная</emphasis>: символы, которые вы вводите с терминала, попадают в систему, а она посылает их назад на терминал, с тем чтобы они появились на экране. Обычно такой процесс подобен эху: символы просто копируются на экран, и вы можете видеть, что вводите, но иногда, например при вводе секретного пароля, эхо пропадает, и символы не появляются на экране.</p>
    <p>Большинство символов на клавиатуре не имеет специального назначения, но есть символы, подсказывающие машине, как интерпретировать то, что вы вводите. Наиболее важным из них является символ, вводимый при нажатии клавиши <emphasis>RETURN</emphasis>. Он обозначает конец вводимой строки: в ответ на его ввод система передвигает курсор на начало следующей строки на экране. Прежде чем система начнет интерпретировать введенные символы, вы должны нажать клавишу <emphasis>RETURN</emphasis>.</p>
    <p><emphasis>RETURN</emphasis> — это пример управляющего символа, т.е. невидимого символа, определяющего некоторые аспекты действий, выполняемых при вводе и выводе на терминал. На обычном терминале символ <emphasis>RETURN</emphasis> вводится с помощью отдельной клавиши, хотя большинство управляющих символов не связано с "персональными" клавишами. Для их ввода требуется нажать клавишу <emphasis>CONTROL</emphasis>, иногда обозначаемую как <emphasis>CTRL</emphasis>, <emphasis>CTL</emphasis> или <emphasis>CNTL</emphasis>, одновременно нажав еще одну клавишу, обычно соответствующую букве. Например, конец строки можно получить, нажав клавишу <emphasis>RETURN</emphasis>, либо (эквивалентный способ) при нажатой клавише <emphasis>CONTROL</emphasis> ввести символ '<emphasis>m</emphasis>' Поэтому <emphasis>RETURN</emphasis> можно называть "управляющее m". или <emphasis>ctl-m</emphasis>. Среди других управляющих символов необходимо выделить <emphasis>ctl-d</emphasis>, сообщающий программе, что ввод окончен: "<emphasis>ctl-g</emphasis>", вызывающий звонок на терминале: <emphasis>ctl-h</emphasis>, который часто называется "шаг назад" и может использоваться при коррекции ошибок ввода, и <emphasis>ctl-i</emphasis> (или <emphasis>tab</emphasis>), перемещающий курсор на следующую позицию табуляции почти так же, как и на стандартном телетайпе. Позиции табуляции в системе UNIX отстоят друг от друга на восемь пробелов. На большинстве терминалов предусмотрены специальные клавиши для символов "шаг назад" и <emphasis>tab</emphasis>.</p>
    <p>Существуют еще две особые клавиши: <emphasis>DELETE</emphasis> (<emphasis>DEL</emphasis>), иногда обозначаемая как <emphasis>RUBOUT</emphasis> (могут использоваться разные сокращения), и <emphasis>BREAK</emphasis> или <emphasis>INTERRUPT</emphasis>. В большинстве систем UNIX ввод символа <emphasis>DELETE</emphasis> немедленно останавливает программу, даже если она еще не завершилась. В некоторых системах для этого употребляется символ <emphasis>ctl-c</emphasis>. В ряде систем в зависимости от способа подключения терминала синонимом <emphasis>DELETE</emphasis> или <emphasis>ctl-c</emphasis> может служить <emphasis>BREAK</emphasis>.</p>
    <subtitle>Сеанс работы с UNIX</subtitle>
    <p>Начнем с диалога между вами и системой UNIX.</p>
    <p>Установить связь: позвонить по телефону или включить питание, если необходимо. Ваша система должна ответить</p>
    <p><code>login: you     </code>Введите ваше имя, затем нажмите <emphasis>RETURN</emphasis></p>
    <p><code>Password:      </code>Ваш пароль, если вы его вводите, не появится на экране</p>
    <p><code>You have mail. </code>Есть почта, ее можно прочесть после входа в систему</p>
    <p><code>$              </code>Система готова к приему ваших команд</p>
    <p><code>$              </code>Можно нажать <emphasis>RETURN</emphasis> несколько раз</p>
    <p><code>$ date         </code>Узнаем дату и время</p>
    <p><code>Sun Sep 25 23:02:57 EDT 1983</code></p>
    <p><code>$ who          </code>Узнаем, кто работает на машине?</p>
    <p><code>jlb  tty0 Sep 25 13:59</code></p>
    <p><code>you  tty2 Sep 25 23:01</code></p>
    <p><code>mary tty4 Sep 25 19:03</code></p>
    <p><code>doug tty5 Sep 25 19:22</code></p>
    <p><code>egb  tty7 Sep 25 17:17</code></p>
    <p><code>bob  tty8 Sep 25 20:48</code></p>
    <p><code>$ mail         </code>Прочтем почту</p>
    <p><code>From doug Sun Sep 25 20:53 EDT 1983</code></p>
    <p><code>зайди ко мне когда-нибудь в понедельник</code></p>
    <p><code>?              </code>При нажатии <emphasis>RETURN</emphasis> выдается следующее сообщение</p>
    <p><code>From mary Sun Sep 25 19:07 EDT 1983</code></p>
    <p><code>Пообедаем завтра в полдень?</code></p>
    <p><code>? d            </code>Удалим это сообщение</p>
    <p><code>$              </code>Почты больше нет</p>
    <p><code>$ mail mary    </code>Пошлем почту mary</p>
    <p><code>обед в 12</code></p>
    <p><code>ctl-d          </code>Конец сообщения</p>
    <p><code>$              </code>Позвоним по телефону или выключим терминал</p>
    <p>Вот и все</p>
    <p>Иногда к такому диалогу сводится вся ваша работа с системой, однако бывает, что при этом и "дело делается". Далее мы разберем приведенный пример сеанса, а также некоторые полезные программы.</p>
    <subtitle>Вход в систему</subtitle>
    <p>Вы должны иметь входное имя и пароль, которые можно получить от администратора системы. Система UNIX может работать с самыми разными терминалами, но в основном она ориентирована на устройства со <emphasis>строчными буквами</emphasis>. Разница между строчными и прописными буквами существенна! Если на вашем терминале есть только прописные буквы (как на некоторых портативных терминалах), это настолько усложнит вам жизнь, что вскоре вы начнете искать другой терминал.</p>
    <p>Перед началом работы проверьте, все ли переключатели на терминале установлены в правильное положение: верхний или нижний регистр, дуплексная ли связь и т.д. с учетом рекомендаций местного специалиста, например в отношении скорости передачи. Затем, используя любые "заключения", установите связь с системой: иногда для этого достаточно лишь позвонить по телефону или просто включить тумблер. В любом случае система должна выдать:</p>
    <p><code>login:</code></p>
    <p>Если она выдала чепуху, то, может быть, у вас установлена не та скорость передачи: проверьте положение соответствующего (а заодно и других) переключателя. Когда это не помогает, нужно медленно несколько раз нажать клавишу <emphasis>BREAK</emphasis> или <emphasis>INTERRUPT</emphasis>. Если приглашение <code>login:</code> и теперь не появляется, вам следует обратиться за помощью к специалисту.</p>
    <p>Получив приглашение на вход, введите ваше входное имя строчными буквами, после чего нажмите клавишу <emphasis>RETURN</emphasis>. При необходимости система запросит у вас пароль, и пока вы вводите его, выдача на терминал будет отключена. В конце концов, войдя в систему, вы получите приглашение (как правило, один символ, означающий, что система готова принимать ваши команды). Приглашением, вероятнее всего, окажется символ <code>$</code> (знак доллара) или <code>%</code> (процент), но можно заменить его любым другим; позднее мы покажем, как это сделать. Приглашение обычно выдается программой, называемой <emphasis>интерпретатором команд</emphasis>, или <code>shell</code>, с помощью которой вы общаетесь с системой. Перед приглашением на экране может появиться сообщение о том, что для вас есть почта, или вопрос о типе применяемого терминала. Ваш ответ поможет системе воспользоваться какими-либо особыми свойствами данного терминала.</p>
    <subtitle>Ввод команд</subtitle>
    <p>Получив приглашение, вы можете ввести команды, т.е. требования к системе что-либо сделать. Мы часто будем употреблять слово <emphasis>программа</emphasis> как синоним команды. Когда на экране появится приглашение (пусть это будет символ <code>'$'</code>), введите <code>date</code> и нажмите клавишу <emphasis>RETURN</emphasis>. Система в ответ выдаст дату и время и выведет следующее приглашение, так что весь диалог на вашем терминале примет вид:</p>
    <p><code>$ date</code></p>
    <p><code>Mon Sep 26 12 : 20 : 57 EDT 1983 $</code></p>
    <p>Не забывайте нажимать клавишу <emphasis>RETURN</emphasis> и не вводите символ <code>'$'</code>. Если вам покажется, что о вас забыли, нажмите <emphasis>RETURN</emphasis>, возможно, что-нибудь и произойдет. В дальнейшем мы не будем напоминать вам о том, что символ <emphasis>RETURN</emphasis> должен завершать каждую строку.</p>
    <p>Затем попробуйте ввести команду <code>who</code>, которая сообщает, кто в данный момент работает в системе:</p>
    <p><code>$ who</code></p>
    <p><code>rim    tty0 Sep 26 11:37</code></p>
    <p><code>pjw    tty4 Sep 26 11:30</code></p>
    <p><code>gerard tty7 Sep 26 10:27</code></p>
    <p><code>mark   tty9 Sep 26 07:59</code></p>
    <p><code>you    ttya Sep 26 12:20</code></p>
    <p><code>$</code></p>
    <p>В первом столбце указывается имя пользователя, во втором — системное имя используемого устройства связи (<code>tty</code> — сокращение от teletype, "архаическое" обозначение терминала). В третьем столбце отмечаются дата и время входа в систему. Вы можете поэкспериментировать и с такой командой:</p>
    <p><code>$ who am i</code></p>
    <p><code>you ttya Sep 26 12:20</code></p>
    <p><code>$</code></p>
    <p>Если вы ошибетесь и укажете при вводе несуществующую команду, система ответит, что команда с этим именем не найдена:</p>
    <p><code>$ whom         </code>Опечатка в имени команды...</p>
    <p><code>whom:not found </code>поэтому система не знает, как запустить ее</p>
    <p><code>$</code></p>
    <p>Конечно, если вы неумышленно введете имя существующей команды, то она начнет выполняться и, возможно, даст неожиданный результат.</p>
    <subtitle>Странное поведение терминала</subtitle>
    <p>Иногда ваш терминал может повести себя странно, например, каждая буква будет выдаваться дважды, или при нажатии клавиши <emphasis>RETURN</emphasis> курсор не переместится в первую позицию следующей строки. Обычно это можно устранить, выключив и включив терминал или выйдя из системы и вновь войдя в нее. Можно также прочитать описание команды <code>stty</code> ("set terminal options" — установка режима терминала) в разделе 1 справочного руководства. Для разумной интерпретации символа <code>tab</code>, если на вашем терминале не предусмотрен режим автоматической табуляции, введите команду</p>
    <p><code>$ stty -tabs</code></p>
    <p>и система будет преобразовывать символ <emphasis>tab</emphasis> в определенное число пробелов. Если же на терминале допускается возможность установки позиций табуляции от машины, то команда <code>tabs</code> поставит их должным образом. (Вы можете дать и такую команду:</p>
    <p><code>$ tabs terminal-type</code></p>
    <p>см. описание команды <code>tabs</code> в справочном руководстве.)</p>
    <p>Ошибки при вводе. Если вы ошиблись при вводе и заметили это прежде, чем нажали клавишу <emphasis>RETURN</emphasis>, то поправить дело можно двумя способами: или стирать символы (но одному), или уничтожить всю строку и ввести ее заново. При нажатии клавиши, служащей для уничтожения строки (обычно это символ <code>'@'</code>), вся строка будет удалена, как будто бы вы ее никогда и не вводили, а вам придется выполнить ввод с начала новой строки:</p>
    <p><code>$ ddate@</code> Уничтожается вся строка:</p>
    <p><code>date</code> ввод с новой строки</p>
    <p><code>Mon Sep 26 12:23:39 EDT 1983</code></p>
    <p><code>$</code></p>
    <p>Символ <code>'#'</code> стирает последний введенный символ: каждый раз при вводе <code>#</code> стирается только один символ слева от курсора. Поэтому если вы не уверены в своем вводе, можно делать поправки по мере работы:</p>
    <p><code>$ dd#atte##e</code> Исправления в процессе ввода</p>
    <p><code>Mon Sep 26 12:24:02 EDT 1983</code></p>
    <p><code>$</code></p>
    <p>Представление символов стирания (<code>#</code>) и уничтожения (<code>@</code>) зависит от системы. Во многих системах (включая используемую здесь для примеров) в качестве символа стирания употребляется "шаг назад", что хорошо смотрится на видеотерминалах. С помощью своей системы вы можете легко проверить, так ли это:</p>
    <p><code>$ datee&#8592;                     </code>Пробуем &#8592;</p>
    <p><code>datee&#8592; : not found           </code>Не подходит</p>
    <p><code>$ datee#                     </code>Пробуем #</p>
    <p><code>Mon Sep 26 12:26:08 EDT 1983 </code># подходит</p>
    <p><code>$</code></p>
    <p>(Мы изображаем "шаг назад" как &#8592;, чтобы символ был "виден".) Обычно принимается, что символ <emphasis>ctl-u</emphasis> уничтожает всю строку.</p>
    <p>Далее в качестве символа стирания мы будем использовать <code>#</code>, поскольку он "виден" на терминале. Если в вашей системе его нет, то сделайте соответствующие изменения. Ниже при описании настройки окружения мы покажем, как задать удобное для вас представление символов стирания и уничтожения.</p>
    <p>А что нужно, чтобы ввести сами символы стирания и уничтожения как часть текста? Если перед символами <code>#</code> и <code>@</code> поставить обратную дробную черту (<code>\</code>), то они утрачивают свое специальное назначение. Поэтому для ввода <code>#</code> или <code>@</code> необходимо набрать на клавиатуре <code>\#</code> или <code>\@</code>. При вводе <code>@</code> система может передвинуть курсор в начало следующей строки, даже если ему предшествовала обратная дробная черта. Не волнуйтесь, символ <code>@</code> в текст попадет.</p>
    <p>Обратную дробную черту иногда называют символом экранирования. Она указывает на то, что следующий за ней символ в некотором смысле специальный. Для удаления обратной дробной черты необходимо ввести два символа стирания: <code>\##</code>. Понятно почему?</p>
    <p>Вводимые вами символы, прежде чем попадут к адресату, анализируются и интерпретируются последовательным рядом программ. Поэтому их интерпретация зависит не только от программы, в которую они попали, но и от того, откуда они туда попали.</p>
    <p>Каждый вводимый символ немедленно, как эхо, появляется на терминале, если, конечно, эхо "не отключено", что бывает редко. До тех пор пока вы не нажали клавишу <emphasis>RETURN</emphasis>, все символы временно сохраняются в ядре системы, так что с помощью символов стирания и уничтожения можно исправить ошибки ввода. Если символ стирания или уничтожения предваряется обратной дробной чертой, то ядро отбрасывает обратную черту и хранит соответствующий символ, не интерпретируя его специальным образом.</p>
    <p>При нажатии клавиши <emphasis>RETURN</emphasis> хранимые символы пересылаются в программу, читающую сейчас символы с терминала. Считывающая программа в свою очередь может интерпретировать символы особым образом: например, интерпретатор <code>shell</code> исключает любую специальную интерпретацию символа, если ему предшествует обратная дробная черта. Мы вернемся к этому вопросу в гл. 3. Пока же следует помнить о том, что ядро системы реагирует лишь на символы уничтожения и стирания и на обратную дробную черту, если она предшествует одному из них. Любые символы, которые остаются после такой обработки, могут интерпретироваться другими программами.</p>
    <cite>
     <subtitle>Упражнение 1.1</subtitle>
     <p>Объясните, что произойдет в случае ввода команды</p>
     <p><code>$ date\@</code></p>
    </cite>
    <cite>
     <subtitle>Упражнение 1.2</subtitle>
     <p>Большинство интерпретаторов <code>shell</code> (кроме версии 7) интерпретирует символ <code>#</code> как начало примечания и игнорирует весь текст от символа <code>#</code> до конца строки. Учитывая это, объясните приведенный ниже диалог. Предполагается, что для стирания также используется символ <code>#:</code></p>
     <p><code>$ date</code></p>
     <p><code>Mon Sep 26 12:39:56 EDT 1983</code></p>
     <p><code>$ # date</code></p>
     <p><code>Mon Sep 26 12:40:21 EDT 1983</code></p>
     <p><code>$\#date</code></p>
     <p><code>$\\#date</code></p>
     <p><code>#date : not found</code></p>
     <p><code>$</code></p>
    </cite>
    <subtitle>Опережающий ввод</subtitle>
    <p>Ядро системы воспринимает ваш ввод по мере того, как вы набираете текст, даже если оно занято чем-то другим, так что можно вводить данные сколь угодно быстро, даже в тот момент, когда какая-либо программа выполняет вывод на терминал. Вводимые в это время символы будут "перемежаться" на экране с выводимыми, но будут сохраняться и интерпретироваться в должном порядке. Вы можете вводить команды одну за другой, не дожидаясь не только конца, но и начала их выполнения.</p>
    <subtitle>Остановка программы</subtitle>
    <p>Выполнение большинства команд можно остановить, нажав клавишу <emphasis>DELETE</emphasis> (УДАЛИТЬ). Чаще всего так же действует и клавиша <emphasis>BREAK</emphasis> (ПРЕРВАТЬ), которая имеется почти на всех терминалах. При работе некоторых программ, таких, как текстовые редакторы, <emphasis>DELETE</emphasis> прекращает выполнение программы, но без выхода из нее. При выключении терминала или отключении от сети выполнение многих программ завершается.</p>
    <p>Если вы хотите приостановить вывод на терминал, например, для того, чтобы важная информация не исчезла с экрана, введите <emphasis>ctl-s</emphasis>. Вывод прекратится практически немедленно: выполнение программы приостановится до тех пор, пока вы не возобновите его, для чего достаточно ввести <emphasis>ctl-q</emphasis>.</p>
    <subtitle>Выход из системы</subtitle>
    <p>Для правильного выхода из системы нужно вместо очередной команды ввести <emphasis>ctl-d</emphasis>. <code>Shell</code> воспримет это как сообщение о конце ввода. (Что произойдет на самом деле, будет объяснено в следующей главе.) Обычно вы можете просто выключить терминал или отключить его от сети, но произойдет ли при этом фактический выход из системы, зависит от самой системы.</p>
    <subtitle>Почта</subtitle>
    <p>Система предоставляет почтовые услуги для обмена сообщениями с другими пользователями, так что, войдя однажды в систему, вы увидите на экране до появления приглашения сообщение:</p>
    <p><code>You have mail</code></p>
    <p>Для чтения почты введите:</p>
    <p><code>$ mail</code></p>
    <p>Сообщения будут выведены одно за другим, начиная с самых последних. После каждого сообщения программа <code>mail</code> ожидает вашего указания, что делать с сообщением. Возможны два основных ответа: ввод символа <code>'d'</code>, означающего удаление сообщения, и ввод <emphasis>RETURN</emphasis>, оставляющего его (т.е. оно вновь появится при следующем просмотре почты). Другими ответами могут быть <code>'p'</code>, что означает распечатку сообщения, <code>'s filename'</code> — сохранение сообщения в поименованном файле и <code>'q'</code> — выход из программы mail. (Если вы не знаете, что такое файл, то представьте его себе как место, где можно хранить информацию под выбранным вами именем, а затем получать ее оттуда. Файлы рассматриваются в разд. 1.2, как, впрочем, и в большей части этой книги.)</p>
    <p><code>mail</code> — именно та программа, которая, вероятно, будет отличаться от описываемой здесь, поскольку существует много вариантов такой программы. Более детально вы можете познакомиться с ней по своему справочному руководству.</p>
    <p>Послать почту кому-нибудь весьма просто. Допустим, она предназначена для пользователя с входным именем <code>nico</code>. Легче всего это сделать так:</p>
    <p><code>$ mail nico</code></p>
    <p><code>Теперь вводите любой текст письма</code></p>
    <p><code>из любого числа строк...</code></p>
    <p><code>После последней строки письма введите ctl-d</code></p>
    <p><code>ctl-d</code></p>
    <p><code>$</code></p>
    <p>Ввод <emphasis>ctl-d</emphasis> означает, что письмо окончено. Если в процессе составления письма вы передумаете и решите его не отправлять, нажмите клавишу <emphasis>DELETE</emphasis> вместо <emphasis>ctl-d</emphasis>. Незаконченное письмо будет сохранено в файле <code>dead.letter</code>.</p>
    <p>Для проверки пошлите письмо самому себе, а затем введите <code>mail</code>, чтобы прочитать его. (Это не так странно, как может показаться, и представляется удобным механизмом напоминания.) Существуют и иные способы посылки почты: можно послать заранее подготовленное письмо, направить почту нескольким адресатам одновременно и даже переслать почту пользователям, работающим на других машинах (подробнее об этом см. в описании команды <code>mail</code> в разд. 1 справочного руководства по UNIX.) В дальнейшем мы будем применять обозначение <code>mail(1)</code> для страницы, описывающей команду <code>mail</code> в разд. 1 справочного руководства.</p>
    <p>Имеется также служебная программа <code>calendar</code> для печати календаря (см. <code>calendar(1)</code>); в гл. 4 мы покажем, как создать такую программу, если она отсутствует.</p>
    <p>Сообщение для других пользователей. Если ваша система UNIX многопользовательская, то как-нибудь однажды на вашем терминале может появиться сообщение типа</p>
    <p><code>Message from mary tty 7...</code></p>
    <p>сопровождаемое пугающим жужжанием. Пользователь <code>Mary</code> хочет что-то сообщить вам, но, если вы не совершите определенных действий, то не сможете ей ответить. Поэтому введите</p>
    <p><code>$ write mary</code></p>
    <p>чтобы установить двустороннюю связь. Теперь вы с Mary сможете обмениваться сообщениями, хотя эта линия связи очень медленная, словно ваш абонент находится на Луне.</p>
    <p>У вас может появиться желание во время выполнения программы задать ту или иную команду для <code>shell</code>. Обычно, какая бы программа ни выполнялась, она должна быть приостановлена либо остановлена но некоторые программы, такие, как редактор или сама команда <code>write</code>, имеют специальную команду <code>'T'</code> для временного выхода в интерпретатор <code>shell</code> (см. табл. 2 приложения 1).</p>
    <p>Команда <code>write</code> не накладывает никаких ограничений, поэтому необходим протокол общения, чтобы ваш ввод не перемешивался с тем, что вводит Mary. Существует соглашение, согласно которому ввод следует осуществлять порциями, оканчивающимися символами (<code>о</code>), что означает конец ввода ("over"), а для сигнализации о прекращении связи использовать (<code>oo</code>) ("over" и "out" — конец и выход).</p>
    <p>Терминал mary                               Ваш терминал</p>
    <p><code>$ write you             $ Message from mary tty7...</code></p>
    <p><code>                        write mary</code></p>
    <p><code>Message from ttya...</code></p>
    <p><code>did you forget lunch?(o)</code></p>
    <p><code>                        did you forget lunch?(o)</code></p>
    <p><code>                        five@</code></p>
    <p><code>                        ten minutes(o)</code></p>
    <p><code>ten minutes(o)</code></p>
    <p><code>ok(oo)</code></p>
    <p><code>                        ok(oo)</code></p>
    <p><code>                        ctl-d</code></p>
    <p><code>EOF</code></p>
    <p><code>ctl-d</code></p>
    <p><code>$                       $ EOF</code></p>
    <p>Выполнение команды <code>write</code> также можно прекратить, нажав клавишу <emphasis>DELETE</emphasis>. Заметьте, что ваши ошибки при вводе не появляются на терминале у Mary.</p>
    <p>Если вы попытаетесь послать сообщение на терминал тому, кто пока еще не вошел в систему или не хочет, чтобы его беспокоили, вас известят об этом. В том случае, когда адресат находится в системе, но не отвечает за разумный промежуток времени (возможно, он занят или отошел от терминала), просто введите <emphasis>ctl-d</emphasis> или <emphasis>DELETE</emphasis>. Если вы не хотите, чтобы вас беспокоили, используйте команду <code>mesg(1)</code>.</p>
    <subtitle>Служба новостей</subtitle>
    <p>Многие системы UNIX имеют службу новостей, чтобы держать пользователей в курсе интересных и не очень интересных событий. Попробуйте ввести</p>
    <p><code>$ news</code></p>
    <p>Существует также большая сеть систем UNIX, которые связаны но телефонному каналу: справьтесь у местного специалиста о командах <code>netnews</code> или USENET.</p>
    <subtitle>Справочное руководство</subtitle>
    <p>В справочном руководстве по UNIX вы найдете большую часть того, что нужно знать о системе. В разд. 1 включены и те команды, которые мы обсуждали в этой главе. В разд. 2 описываются системные обращения, которые будут темой гл. 7, а в разд. 6 приводится информация об играх. В остальных разделах описываются функции, применяемые пользователями, программирующими на языке Си, а также структура файлов и средства поддержания работоспособности системы. (Число разделов варьируется от системы к системе.) Не забывайте о предметном указателе в начале руководства: вам достаточно бегло просмотреть его, чтобы найти команды, необходимые для вашей работы. Кроме того, существует введение в систему, где дается обзор ее функциональных возможностей. Часто справочное руководство хранится в системе и его можно читать с терминала. Если вам требуется помощь и не к кому обратиться, можно вывести на терминал любую страницу руководства по системе, введя команду man command-name. Так, чтобы получить информацию о команде who, введите</p>
    <p><code>$ man who</code></p>
    <p>и, конечно,</p>
    <p><code>$ man man</code></p>
    <p>выдаст вам сведения о самой команде <code>man</code>.</p>
    <subtitle>Автоматизированный справочник</subtitle>
    <p>В вашей системе может быть команда с именем <code>learn</code>, которая реализует автоматизированный справочник по системе файлов, основным командам, редактору, программам подготовки документации и даже языку программирования Си. Введите</p>
    <p><code>$ learn</code></p>
    <p>Если эта команда существует в вашей системе, то она подскажет вам, что делать дальше. Если же ее нет, попробуйте ввести еще <code>teach</code>.</p>
    <subtitle>Игры</subtitle>
    <p>Они не всегда признаются официально, но это один из лучших способов обрести уверенность в общении с машиной и терминалом. Сама система UNIX имеет достаточно скромный набор игр, но он часто пополняется программистами на местах. Поспрашивайте вокруг или обратитесь к разд. 6 своего справочного руководства.</p>
   </section>
   <section>
    <title>
     <p>1.2 Повседневная работа: файлы и основные команды</p>
    </title>
    <p>Информация в системе UNIX хранится в <emphasis>файлах</emphasis>, которые весьма похожи на обычные учрежденческие "дела". Каждый файл имеет имя, содержание, место, где он хранится, и некоторую служебную информацию, в частности о владельце файла, размере последнего и т.д. Файл может содержать письмо, список имен и адресов, операторы текста программы, данные, которые будет использовать программа, или даже программы в форме, готовой к выполнению, а также другую нетекстовую информацию.</p>
    <p>Файловая система UNIX устроена таким образом, что вы можете хранить ваши личные файлы, не смешивая их с файлами других пользователей, причем те в свою очередь не смогут повлиять на ваши файлы. Существует бесчисленное множество программ, оперирующих с файлами, но пока мы остановимся на наиболее часто используемых. В гл. 2 дается последовательное изложение файловой системы и вводится много команд, с которыми приходится иметь дело при работе с файлами.</p>
    <subtitle>Создание файлов. Редактор</subtitle>
    <p>Если вы хотите ввести статью, письмо или программу, как заставить машину хранить информацию? Большинство таких задач решается с помощью <emphasis>текстового редактора</emphasis>, т. е. программы для ввода и обработки информации в машине. Практически в любой системе UNIX есть <emphasis>экранный редактор</emphasis> — редактор, который использует возможности современных терминалов отображать результат редактирования но мере корректирования текста. Наиболее популярны редакторы vi и emacs. Однако мы не будем описывать здесь какой-либо конкретный экранный редактор частично из-за сложностей, связанных с типографским набором, частично из-за отсутствия стандартного редактора.</p>
    <p>Но существует старый редактор <code>ed</code>, который, без сомнения, имеется в вашей системе. Его можно использовать на любом терминале. Он содержит основу, на базе которой строятся другие важные программы (включая и некоторые экранные редакторы), поэтому все-таки следует его изучить. Подробное описание ed приводится в приложении 1.</p>
    <p>Какой бы редактор вы ни предпочли, вам придется изучить его настолько, чтобы с его помощью уметь создавать файлы. Мы будем работать с редактором <code>ed</code>, что позволит нам сделать изложение более конкретным, а вам — выполнить приводимые здесь примеры в своей системе, хотя в принципе вы можете использовать любой редактор.</p>
    <p>Попытайтесь создать с помощью <code>ed</code> файл под именем <code>junk</code> следующим образом:</p>
    <p><code>$ ed   </code>Вызов текстового редактора</p>
    <p><code>а      </code>команда редактора для добавления текста (add)</p>
    <p><code>now type in</code></p>
    <p><code>whatever text you want...</code></p>
    <p><code>.      </code>Ввод только '.' прекращает добавление</p>
    <p><code>w junk </code>Запись текста в файл с именем junk</p>
    <p><code>39     </code>ed сообщает число введенных символов</p>
    <p><code>q      </code>Выход из ed (quit)</p>
    <p><code>$</code></p>
    <p>Команда <code>'a'</code> (append — добавить) сообщает редактору, что нужно принять текст. Сигналом окончания текста служит один символ <code>'.'</code>, который должен быть введен в начале строки. Не забывайте об этом, поскольку пока он не введен, не распознаются никакие команды редактора, т. е. все, что вы вводите, будет трактоваться как продолжение вводимого текста.</p>
    <p>Команда редактора <code>'w'</code> (write — писать) сохранит введенную информацию: <code>'w junk'</code> запишет ее в файл с именем <code>junk</code>. Именем файла может быть любое слово. Мы выбрали <code>junk</code>, чтобы показать, что этот файл не очень важен ("junk" — мусор).</p>
    <p>Редактор сообщает системе число символов, записанных им в файл. До ввода команды <code>'w'</code> ничего не отправляется на постоянное хранение, поэтому, если вы отключите свой компьютер от сети и пойдете домой, информация не попадет в файл. (Если вы это сделаете во время редактирования, информация, с которой вы работаете, будет сохранена в файле <code>ed.hup</code>, и в дальнейшем можно будет продолжить работу.) В случае аварии системы в процессе редактирования (т.е. неожиданного останова из-за неисправности аппаратуры или ошибок в программном обеспечении) ваш файл сохранит только то, что в него записала последняя команда write. Но после выполнения команды <code>w</code> информация хранится постоянно. Она может стать доступной, если вы введете</p>
    <p><code>$ ed junk</code></p>
    <p>Конечно, можно редактировать введенный текст, чтобы исправить опечатки, заменить слова, переставить части текста и т.д. Когда вы завершите редактирование, команда <code>'q'</code> ("quit" — выход) осуществит выход из редактора.</p>
    <subtitle>Что за файлы здесь?</subtitle>
    <p>Чтобы знать, с чем приходится иметь дело, создадим два файла с именами <code>junk</code> и <code>temp</code>:</p>
    <p><code>$ ed</code></p>
    <p><code>а</code></p>
    <p><code>То be or not to be</code></p>
    <p><code>.</code></p>
    <p><code>w junk</code></p>
    <p><code>19</code></p>
    <p><code>q</code></p>
    <p><code>$ ed</code></p>
    <p><code>a</code></p>
    <p><code>What is a question.</code></p>
    <p><code>.</code></p>
    <p><code>w temp</code></p>
    <p><code>22</code></p>
    <p><code>q</code></p>
    <p><code>$</code></p>
    <p>Число символов, сообщаемое редактором <code>ed</code>, включает и специальный символ в конце каждой строки, называемый <emphasis>перевод строки</emphasis> или <emphasis>конец строки</emphasis>, — так система представляет символ <emphasis>RETURN</emphasis>.</p>
    <p>Команда <code>ls</code> перечисляет имена (но не содержание) файлов:</p>
    <p><code>$ ls</code></p>
    <p><code>junk</code></p>
    <p><code>temp</code></p>
    <p><code>$</code></p>
    <p>Они и являются, действительно, именами двух только что созданных файлов (могут быть также и другие, которые вы не создавали сами). Имена автоматически сортируются в алфавитном порядке.</p>
    <p>Как и большинство команд, <code>ls</code> имеет возможные аргументы, которые позволяют изменить ее действие. Возможные аргументы следуют за именем команды в командной строке и обычно состоят из знака минус и одной буквы, несущей смысловую нагрузку. Например, команда <code>ls -t</code> требует перечисления файлов во временном порядке, т.е. в зависимости от времени последнего изменения файла, причем файлы, измененные последними, перечисляются вначале:</p>
    <p><code>$ ls -t</code></p>
    <p><code>temp</code></p>
    <p><code>junk</code></p>
    <p><code>$</code></p>
    <p>Возможный аргумент <code>-l</code> означает "длинный" список (long — длинный), который содержит больший объем информации о каждом файле:</p>
    <p><code>$ ls -l</code></p>
    <p><code>total 2</code></p>
    <p><code>-rw-r--r-- 1 you 17 Sep 26 16:25 junk</code></p>
    <p><code>-rw-r--r-- 1 you 18 Sep 26 16:26 temp</code></p>
    <p><code>$</code></p>
    <p>Строка <code>total 2</code> указывает число занятых блоков на диске: блок обычно содержит 512 или 1024 символа. Строка <code>-rw-r--r--</code> показывает, кто имеет право читать из файла и писать в него: в данном случае владелец (<code>you</code>) может и читать, и писать, но другие могут только читать. За ней следует 1 — число связей файла; забудем о нем до гл. 2. Строка <code>you</code> содержит имя владельца файла, т.е. пользователя, создавшего его. Число символов в соответствующих файлах (17 и 18) совпадает с тем, что сообщил редактор <code>ed</code>. Дата и время соответствуют последнему изменению файла.</p>
    <p>Возможные аргументы (в дальнейшем будем именовать их флагами) могут быть сгруппированы: <code>ls -lt</code> дает ту же информацию, но отсортированную в определенном порядке, начиная с файлов, измененных последними. Флаг <code>-r</code> показывает дату и время последнего обращения к файлу; <code>ls -lut</code> представляет список файлов по порядку их использования, начиная с наиболее позднего. Флаг <code>-r</code> меняет порядок в списке на обратный, так что <code>ls -rt</code> перечисляет файлы, начиная с самых старых. Можно также указать имена интересующих вас файлов, тогда команда <code>ls</code> выдаст информацию только о них:</p>
    <p><code>$ ls -l junk</code></p>
    <p><code>-rw-r--r-- 1 you 17 Sep 26 16:25</code></p>
    <p><code>$</code></p>
    <p>Строки, следующие за именем команды в командной строке, такие, как <code>-l</code> и <code>junk</code> в приведенном примере, называются <emphasis>аргументами</emphasis> команды. Аргументы обычно бывают флагами или именами файлов, используемыми в команде.</p>
    <p>Обозначение флага с помощью знака "дефис" и одной буквы, например <code>-l</code>, является весьма распространенным. В общем случае, если команда имеет возможные аргументы, то они должны предшествовать аргументам-именам файлов, но появляться могут в любом порядке. Однако система UNIX "капризна" при разборе многочисленных флагов. Например, в стандартной седьмой версии системы команда <code>ls</code> не принимает строку</p>
    <p><code>$ ls -l-t</code> Не работает в седьмой версии</p>
    <p>в качестве синонима для <code>ls -lt</code>, тогда как другие команды требуют, чтобы флаги были разделены.</p>
    <p>По мере изучения системы вы обнаружите, что здесь нет регулярного или систематического подхода к флагам. У каждой команды есть свои "причуды" и свой набор букв для флагов (часто отличающийся от применяемых для выполнения той же функции в другой команде). Такое непредсказуемое поведение системы несколько смущает, и на него часто указывают как на основной недостаток, хотя в новых версиях системы предусмотрено большее единообразие. Пока же мы можем посоветовать вам лучше продумывать флаги в своих программах и на всякий случай держать под рукой копию справочного руководства.</p>
    <subtitle>Печать файлов. Команды <code>cat</code> и <code>pr</code></subtitle>
    <p>Теперь, когда у нас есть файлы, как посмотреть их содержимое? Существует множество программ, решающих эту задачу (возможно, даже слишком много). Один из вариантов — использование редактора:</p>
    <p><code>$ ed junk</code></p>
    <p><code>19                 </code>ed сообщает, что в файле 17 символов</p>
    <p><code>1,$ p              </code>Печать от первой до последней строки</p>
    <p><code>То be or not to be </code>В файле только одна строка</p>
    <p><code>q                  </code>Все сделано</p>
    <p>Редактор начинает работу с сообщения числа символов в файле <code>junk</code>: команда <code>'1,$ p'</code> инициирует вывод всех строк файла. После того как вы научитесь пользоваться редактором, вы сможете выбирать части файла, предназначенные для печати. Но бывают ситуации, когда невозможно использовать редактор для печати. Например, размер файла, с которым может работать редактор, имеет определенный предел (несколько тысяч строк). Далее, он может вывести на печать только один файл в данный момент, а нужно печатать несколько, один за другим без перерыва. В таких ситуациях существует несколько способов просмотра файлов.</p>
    <p>Прежде всего есть программа <code>cat</code> (самая простая из программ печати), которая выдает содержимое всех файлов, указанных как аргументы:</p>
    <p><code>$ cat junk</code></p>
    <p><code>To be or not to be</code></p>
    <p><code>$ cat temp</code></p>
    <p><code>That is a question.</code></p>
    <p><code>$ cat junk temp</code></p>
    <p><code>To be or not to be</code></p>
    <p><code>That is a question.</code></p>
    <p><code>$</code></p>
    <p>Поименованный файл или файлы "катенируются" (отсюда и имя <code>cat</code>), т.е. выводятся на терминал последовательно один за другим без промежутков.</p>
    <p>С короткими файлами никаких проблем нет, но в случае длинных файлов, если ваш терминал соединен с машиной высокоскоростной линией, вы должны быть достаточно проворны, чтобы с помощью <emphasis>ctl-s</emphasis> остановить вывод прежде, чем он исчезнет с экрана. Стандартной команды для выдачи файла на видеотерминал порциями размеров в экран не существует, хотя в каждой системе UNIX такая команда имеется. В вашей системе это может быть команда <code>pg</code> или <code>more</code>. Здесь она называется <code>pr</code> в гл. VI будет показана ее реализация.</p>
    <p>Подобно команде <code>cat</code>, <code>pr</code> выдает содержимое всех файлов, перечисленных в списке, но в виде, подходящем для устройства печати: каждая страница длиной в 11 дюймов содержит 66 строк, включая заголовок, где указываются номер страницы, имя файла, дата и время его последнего изменения. В месте сгиба бумаги строки пропускаются. Итак, для того чтобы красиво напечатать файл <code>junk</code>, затем перейти на следующую страницу и так же красиво напечатать файл <code>temp</code>, задайте:</p>
    <p><code>$ pr junk temp</code></p>
    <p><code>Sep 26 16:25 1983 junk Page 1</code></p>
    <empty-line/>
    <p><code>To be or not to be</code></p>
    <p>(еще 60 пустых строк)</p>
    <p><code>Sep 26 16:26 1983 temp Page 1</code></p>
    <empty-line/>
    <p><code>That is a question.</code></p>
    <p>(еще 60 пустых строк)</p>
    <p><code>$</code></p>
    <p>Команда <code>pr</code> может также инициировать печать в несколько столбцов. Так,</p>
    <p><code>$ pr -3 filenames</code></p>
    <p>печатает каждый файл в три столбца. Можно заменить число 3 любым разумным числом, и команда <code>pr</code> "постарается" исполнить задание. (Под <code>filename</code> подразумевается список имен файлов.) Команда <code>pr -m</code> напечатает набор файлов параллельными столбцами, см. <code>pr(1)</code>.</p>
    <p>Следует отметить, что <code>pr</code> — это не программа форматирования текста: она не разбивает текст на строки и не выравнивает поля. Настоящими программами форматирования являются <code>troff</code> и <code>nroff</code>, которые обсуждаются в гл. 9.</p>
    <p>Существуют также команды, которые производят вывод на высокоскоростное печатающее устройство. Поищите в вашем руководстве команду с именем <code>lp</code> или <code>lpr</code> или посмотрите в предметном указателе (индексе) слово "printer". Выбирайте команду в зависимости от того, какое печатающее устройство подключено к вашей машине. Часто команды <code>pr</code> и <code>lpr</code> используются совместно. После того как <code>pr</code> отформатирует информацию должным образом, <code>lpr</code> будет управлять процессом передачи па печатающее устройство. Мы вернемся к этому вопросу позднее.</p>
    <subtitle>Пересылка, копирование и удаление файлов. Команды <code>mv</code>, <code>cp</code>, <code>rm</code></subtitle>
    <p>Рассмотрим другие команды. Вначале попробуем изменить имя файла. Переименование файла производится "пересылкой" (moving) его от одного имени к другому следующим образом:</p>
    <p><code>$ mv junk special</code></p>
    <p>Это означает, что файл с именем <code>junk</code> будет называться теперь <code>special</code>, содержимое его не меняется. Если теперь выполнить команду <code>ls</code>, то вы увидите другой список, в котором нет файла <code>junk</code>, но есть файл <code>special</code>:</p>
    <p><code>$ ls</code></p>
    <p><code>special</code></p>
    <p><code>temp</code></p>
    <p><code>$ cat junk</code></p>
    <p><code>cat: can't open junk</code></p>
    <p><code>$</code></p>
    <p>Будьте осторожны: если вы перешлете файл на место уже существующего файла, то последний будет замещен.</p>
    <p>Чтобы иметь копию файла (т.е. две его версии), воспользуйтесь командой <code>cp</code>:</p>
    <p><code>$ cp special special.save</code></p>
    <p>которая продублирует файл <code>special</code> в <code>special.save</code>. Наконец, когда вы устанете создавать и пересылать файлы, команда rm уберет все указанные файлы:</p>
    <p><code>$ rm temp junk</code></p>
    <p><code>rm: junk nonexistent</code></p>
    <p><code>$</code></p>
    <p><code>$ cp special special.save</code></p>
    <p>Вы получите предупреждение, если один из удаляемых файлов не существует, но в противном случае <code>rm</code>, как и большинство команд UNIX, отработает без лишних слов. Системе не свойственна "болтовня". Приглашения, сообщения об ошибках кратки и не всегда помогают. Краткость может огорчать новичков, но опытных пользователей раздражают "разговорчивые" команды.</p>
    <subtitle>Чем может быть имя файла?</subtitle>
    <p>До сих пор мы употребляли имена файлов, даже не упоминая о том, что является законным именем файла. Теперь пора ввести несколько правил. Во-первых, имя файла ограничено 14 символами.<a l:href="#n2" type="note">[2]</a> Во-вторых, хотя и можно использовать практически любой символ в имени файла, здравый смысл подсказывает, что следует употреблять только видимые символы и избегать применения символов, несущих определенную смысловую нагрузку. Например, как вы уже видели, в команде <code>ls</code> флаг <code>-t</code> означает список, упорядоченный по времени, так что если у вас есть файл с именем <code>-t</code>, вам придется очень постараться, чтобы он попал в список. (Как, действительно, это сделать?) Кроме знака "минус", есть и другие символы, имеющие специальный смысл в первой позиции, однако пока вы не освоите систему, лучше использовать на этом месте только буквы, цифры, точку и символ подчеркивания. (Точка и символ подчеркивания по традиции употребляются для разбития имени файла на части, как в случае <code>special.save</code>). Наконец, не забывайте о различии прописных и строчных букв: <code>junk</code>, <code>JUNK</code> и <code>Junk</code> — разные имена файлов.</p>
    <subtitle>Группа полезных команд</subtitle>
    <p>Поскольку у нас уже есть основные средства создания файлов, выдачи списка имен файлов, печати их содержимого, мы можем рассмотреть и другие команды обработки файлов. Чтобы изложение было конкретным, будем использовать файл <code>poem</code>, который содержит известное стихотворение Августа Де Моргана. Создадим его с помощью редактора <code>ed</code>:</p>
    <p><code>$ ed а</code></p>
    <p><code>Great fleas have little fleas</code></p>
    <p><code> upon their backs to bite 'em,</code></p>
    <p><code>And little fleas have lesser fleas,</code></p>
    <p><code> and so ad infinitum.</code></p>
    <p><code>And the great fleas themselves, in turn,</code></p>
    <p><code> have a greater fleas to go on;</code></p>
    <p><code>While these again have greater still,</code></p>
    <p><code> and greater still, and so on.</code></p>
    <p><code>.</code></p>
    <p><code>w poem 263</code></p>
    <p><code>q</code></p>
    <p><code>$</code></p>
    <p>Начнем с первой команды, которая подсчитывает число строк, слов и символов в одном или нескольких файлах и называется <code>wc</code> по одной из ее функций — подсчета слов ("word counting"):</p>
    <p><code>$ wc poem</code></p>
    <p><code>8 46 263 poem</code></p>
    <p><code>$</code></p>
    <p>т.е. в файле <code>poem</code> восемь строк, 46 слов и 263 символа. Определение "слова" весьма просто — любая последовательность символов, не содержащая пробела, символа табуляции или перевода строки. Команда <code>wc</code> произведет подсчет более чем в одном файле (и сообщит итог) и при необходимости "умолчит" о любом счетчике, см. <code>wc(1)</code>.</p>
    <p>Вторая команда, <code>grep</code>, отыскивает в файлах строки, которые подходят под шаблон (ее имя происходит от имени команды редактора <code>ed g/<emphasis>regular-expression</emphasis>/p</code>, которая объясняется в приложении 1). С помощью этой команды можно найти слово <code>"fleas"</code> в файле <code>poem</code>:</p>
    <p><code>$ grep fleas poem</code></p>
    <p><code>Great fleas have a little fleas</code></p>
    <p><code>And little fleas have lesser fleas,</code></p>
    <p><code>And the great fleas themselves, in turn,</code></p>
    <p><code> have greater fleas to go on;</code></p>
    <p><code>$</code></p>
    <p>Команда <code>grep</code> может также отыскивать строки, которые не соответствуют шаблону, если используется флаг <code>-v</code>. (Флаг назван по имени команды редактора <code>ed</code>; действие флага можно представить как инвертирование условия соответствия шаблону.)</p>
    <p><code>$ grep -v fleas poem</code></p>
    <p><code> upon their backs to bite 'em,</code></p>
    <p><code> and so ad infinitum.</code></p>
    <p><code>While these again have greater still,</code></p>
    <p><code> and greater still, and so on.</code></p>
    <p><code>$</code></p>
    <p>Команду <code>grep</code> можно использовать для поиска в нескольких файлах: в таком случае она будет выдавать имя файла перед каждой строкой, подходящей под шаблон, что позволяет найти место, где произошло сопоставление. Существуют также флаги для нумерации строк и т.д. Команда может применяться и для сопоставления с более сложными шаблонами, чем <code>"fleas"</code>, но об этом речь пойдет в гл. 4.</p>
    <p>Далее рассмотрим команду <code>sort</code>, которая сортирует/входные данные в алфавитном порядке последовательно строку за строкой. Выполним сортировку для файла poem, что не очень интересно, но зато наглядно:</p>
    <p><code>$ sort poem</code></p>
    <p><code> and greater still, and so on.</code></p>
    <p><code> and so ad infinitum,</code></p>
    <p><code> have greater fleas to go on;</code></p>
    <p><code> upon their backs to bite 'em,</code></p>
    <p><code>And little fleas have lesser fleas,</code></p>
    <p><code>And the great fleas themselves, in turn,</code></p>
    <p><code>Great fleas have little fleas</code></p>
    <p><code>While these again have greater still,</code></p>
    <p><code>$</code></p>
    <p>Сортируются все строки, и по умолчанию вначале следуют строки, начинающиеся с пробела, за ними — начинающиеся с прописных букв, затем — со строчных букв, так что здесь не выдерживается строго алфавитный порядок.</p>
    <p>У команды <code>sort</code> есть множество флагов для управления порядком сортировки: обратным, числовым, словарным, с игнорированием начальных пробелов, с сортировкой полей внутри строки и т.п., но, конечно, нужно изучить эти флаги, чтобы уверенно пользоваться ими. Приведем несколько наиболее употребительных флагов:</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>sort -r</code></td>
      <td align="left" valign="top">Обратный порядок</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>sort -n</code></td>
      <td align="left" valign="top">Числовой порядок</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>sort -nr</code></td>
      <td align="left" valign="top">Обратный числовой порядок</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>sort -f</code></td>
      <td align="left" valign="top">Не учитывать различие прописных и строчных букв</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>sort +n</code></td>
      <td align="left" valign="top">Начать сортировку с поля <code>n+1</code></td>
     </tr>
    </table>
    <p>В гл. 4 приводится дополнительная информация о команде <code>sort</code>.</p>
    <p>Следующая команда для работы с файлом — <code>tail</code>; она выдает 10 последних строк файла. Этого более чем достаточно для файла <code>poem</code> и полезно для больших файлов. В команде есть флаг, указывающий число выдаваемых строк, так что для печати последней строки файла poem можно задать:</p>
    <p><code>$ tail -1 poem</code></p>
    <p><code>and greater still, and so on</code></p>
    <p><code>$</code></p>
    <p>Команду <code>tail</code> можно использовать и для вывода файла, начиная с указанной строки:</p>
    <p><code>$ tail +3 filename</code></p>
    <p>файл будет печататься с третьей строки (обратите внимание на естественное изменение знака у аргумента).</p>
    <p>Последняя пара команд предназначена для сравнения файлов. Допустим, имеется вариант файла <code>poem</code> с именем <code>new_poem</code>:</p>
    <p><code>$ cat poem</code></p>
    <p><code>Great fleas have little fleas</code></p>
    <p><code> upon their backs to bite 'em,</code></p>
    <p><code>And little fleas have lesser fleas,</code></p>
    <p><code> and so ad infinitum.</code></p>
    <p><code>And the great fleas themselves, in turn,</code></p>
    <p><code> have greater fleas to go on;</code></p>
    <p><code>While these again have greater still,</code></p>
    <p><code> and greater still, and so on.</code></p>
    <p><code>$ cat new_poem</code></p>
    <p><code>Great fleas have little fleas</code></p>
    <p><code> upon their backs to bite them,</code></p>
    <p><code>And little fleas have lesser fleas,</code></p>
    <p><code> and so on ad infinitum.</code></p>
    <p><code>And the great fleas themselves, in turn,</code></p>
    <p><code> have greater fleas to go on;</code></p>
    <p><code>While these again have greater still,</code></p>
    <p><code> and greater still, and so on.</code></p>
    <p><code>$</code></p>
    <p>Между этими двумя файлами немного различий; на самом деле, нужно постараться, чтобы найти их. Здесь помогут команды сравнения файлов. Команда <code>cmp</code> находит первое место, где файлы различаются:</p>
    <p><code>$ cmp poem new_poem</code></p>
    <p><code>poem new_poem differ: char 58, line 2</code></p>
    <p><code>$</code></p>
    <p>Как видите, файлы различаются во второй строке, но неизвестно, в чем состоит их различие, и, кроме того, не отмечены другие различия. Вторая команда сравнения файлов <code>diff</code> сообщает обо всех строках, которые изменены, добавлены или удалены:</p>
    <p><code>$ diff poem new_poem</code></p>
    <p><code>2c2</code></p>
    <p><code>&lt; upon their backs to bite 'em,</code></p>
    <p><code>---</code></p>
    <p><code>&gt; upon their backs to bite them,</code></p>
    <p><code>4c4</code></p>
    <p><code>&lt; and so ad infinitum.</code></p>
    <p><code>---</code></p>
    <p><code>&gt; and so on ad infinitum.</code></p>
    <p><code>$</code></p>
    <p>Итак, вторая строка первого файла <code>poem</code> изменена и отличается от второй строки второго файла <code>new_poem</code>. То же самое мы наблюдаем и в отношении четвертой строки.</p>
    <p>Вообще, команда <code>cmp</code> применяется в тех случаях, когда вы хотите убедиться, что два файла действительно имеют одинаковое содержимое. Это быстродействующая команда, которая работает с любыми (не только с текстовыми) файлами. Командой <code>diff</code> следует пользоваться, если вы предполагаете, что файлы различны, и хотите узнать, в каких именно строках они различаются. Команда работает только с текстовыми файлами.</p>
    <subtitle>Сводка команд файловой системы</subtitle>
    <p>В табл. 1.1 дана краткая сводка описания команд, которые были рассмотрены выше.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>ls</code></td>
      <td align="left" valign="top">Вывод списка имен файлов текущего каталога</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ls filenames</code></td>
      <td align="left" valign="top">Вывод списка только поименованных файлов</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ls -t</code></td>
      <td align="left" valign="top">Вывод списка, упорядоченного по времени создания файла (сначала более новые)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ls -l</code></td>
      <td align="left" valign="top">Вывод данного списка, содержащего большую информацию; допустимо также <code>ls -lt</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ls -u</code></td>
      <td align="left" valign="top">Вывод списка, упорядоченного по времени последнего использования; допустимо также <code>ls -lu</code>, <code>ls -lut</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ls -r</code></td>
      <td align="left" valign="top">Вывод списка с обратным порядком; допустимо также <code>ls -rt</code>, <code>ls -rit</code> и т.п.</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ed filename</code></td>
      <td align="left" valign="top">Редактирование поименованного файла</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>cp file1 file2</code></td>
      <td align="left" valign="top">Копирование <code>file1</code> в <code>file2</code>, старое содержимое <code>file2</code> пропадает, если оно было</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>mv file1 file2</code></td>
      <td align="left" valign="top">Переименование <code>file1</code> в <code>file2</code>; старый <code>file2</code> исчезает, если он был</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>rm filenames</code></td>
      <td align="left" valign="top">Удаление поименованных файлов безвозвратно</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>cat filenames</code></td>
      <td align="left" valign="top">Вывод содержимого поименованных файлов</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>pr filenames</code></td>
      <td align="left" valign="top">Печать содержимого файлов с заголовком, по 66 строк на странице</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>pr -n filenames</code></td>
      <td align="left" valign="top">Печать в <code>n</code> столбцов</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>pr -m filenames</code></td>
      <td align="left" valign="top">Печать поименованных файлов в несколько столбцов</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>wc filenames</code></td>
      <td align="left" valign="top">Подсчет числа строк, слов и символов для каждого файла</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ws -l filenames</code></td>
      <td align="left" valign="top">Подсчет числа строк для каждого файла</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>grep pattern filenames</code></td>
      <td align="left" valign="top">Вывод строк, соответствующих шаблону</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>grep -v pattern files</code></td>
      <td align="left" valign="top">Вывод строк, не соответствующих шаблону</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>sort filenames</code></td>
      <td align="left" valign="top">Сортировка файлов по строкам в алфавитном порядке</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>tail filename</code></td>
      <td align="left" valign="top">Вывод 10 последних строк файла</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>tail -n filename</code></td>
      <td align="left" valign="top">Вывод <code>n</code> последних строк файла</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>tail +n filename</code></td>
      <td align="left" valign="top">Вывод файла, начиная со строки <code>n</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>cmp file1 file2</code></td>
      <td align="left" valign="top">Вывод места первого расхождения</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>diff file1 file2</code></td>
      <td align="left" valign="top">Вывод всех расхождений между файлами</td>
     </tr>
    </table>
    <p>Таблица 1.1: <strong>Сводка команд файловой системы</strong></p>
   </section>
   <section>
    <title>
     <p>1.3 Продолжаем изучать файлы: каталоги</p>
    </title>
    <p>Система отличит ваш файл с именем <code>junk</code> от "нужного" файла с тем же именем. Это обеспечивается за счет группировки файлов в <emphasis>каталоги</emphasis> подобно тому, как книги помещаются на полках в библиотеке, так что файлы могут иметь одинаковые имена в разных каталогах без конфликтов.</p>
    <p>В общем случае каждый пользователь имеет свой личный каталог, иногда называемый начальным каталогом, который содержит только принадлежащие ему файлы. Входя в систему, вы оказываетесь в вашем личном (начальном) каталоге. Можно сменить каталог, с которым вы работаете (его часто называют рабочим или <emphasis>текущим каталогом</emphasis>), но ваш личный каталог останется тем же. Если не предпринять специальных действий, новый файл, создаваемый вами, попадает в текущий каталог. Так как вначале текущим является личный каталог, ваш файл никак не связан с файлом, имеющим то же имя, в чужом каталоге.</p>
    <p>Каталог может содержать и другие каталоги как обычные файлы. Естественным способом представления такой организации каталогов служит дерево файлов и каталогов. В процессе обхода дерева — от корня вдоль нужных ветвей — можно найти любой файл системы. Можно поступить и наоборот: начать в произвольном месте и двигаться по направлению к корню.</p>
    <p>Рассмотрим первый способ. Основным нашим средством будет команда <code>pwd</code> ("print working directory" — печать рабочего каталога), которая выведет имена файлов каталога, с которым вы работаете:</p>
    <p><code>$ pwd</code></p>
    <p><code>/usr/you</code></p>
    <p><code>$</code></p>
    <p>Команда выведет сообщение о том, что вы находитесь в каталоге you, а сам каталог — в каталоге <code>usr</code>, который в свою очередь находится в корневом каталоге, традиционно обозначаемом как <code>'/'</code>. Символ <code>'/'</code> разделяет компоненты имени: каждый компонент ограничен по длине 14 символами. Во многих системах каталог <code>/usr</code> содержит имена каталогов всех пользователей. (Даже если ваш личный каталог не <code>/usr/you</code>, команда <code>pwd</code> выдаст нечто аналогичное, так что вы сможете следить за последующими примерами.) Введя</p>
    <p><code>$ ls /usr/you</code></p>
    <p>вы получите тот же самый список файлов, который выдает только <code>ls</code>. Если в команде <code>ls</code> нет аргументов, то она выводит содержимое текущего каталога; если же ей присвоить имя каталога, то она выдает содержимое указанного каталога. Далее вводите</p>
    <p><code>$ ls /usr</code></p>
    <p>Это приведет к появлению длинного списка имен, среди которых есть и ваш начальный каталог <code>you</code>.</p>
    <p>На следующем шаге попытайтесь распечатать сам корневой каталог. В результате получите ответ, подобный следующему:</p>
    <p><code>$ ls /</code></p>
    <p><code>bin</code></p>
    <p><code>boot</code></p>
    <p><code>dev</code></p>
    <p><code>etc</code></p>
    <p><code>lib</code></p>
    <p><code>tmp</code></p>
    <p><code>unix</code></p>
    <p><code>usr</code></p>
    <p><code>$</code></p>
    <p>(Пусть вас не смущает то, что символ <code>'/'</code> имеет два назначения одновременно: имя корневого каталога и разделитель в именах файлов.) Большую часть приведенного списка составляют имена каталогов, но <code>unix</code> на самом деле является файлом, содержащим в готовом к выполнению виде ядро системы UNIX (более подробно об этом см. в гл. 2).</p>
    <p>Теперь попробуйте ввести</p>
    <p><code>$ cat /usr/you/junk</code></p>
    <p>(если файл <code>junk</code> все еще хранится в вашем каталоге). Имя <code>/usr/you/junk</code> называется путевым, или <emphasis>абсолютным</emphasis>, <emphasis>именем</emphasis> файла. Путевое имя имеет интуитивный смысл: оно представляет путь по дереву каталогов от корня к отдельному файлу. В системе UNIX есть универсальное правило: всюду, где можно использовать обычное имя файла, можно использовать и абсолютное имя.</p>
    <p>Файловая система имеет структуру, подобную генеалогическому дереву:</p>
    <image l:href="#img_1.jpeg"/>
    <p><emphasis>Рис. 1.1</emphasis>: Карта файловой системы UNIX</p>
    <empty-line/>
    <p>Ваш файл с именем <code>junk</code> никак не связан с файлами пользователей <code>paul</code> или <code>mary</code>.</p>
    <p>Абсолютные имена не слишком впечатляют, если все интересующие вас файлы находятся в вашем каталоге, но если вы работаете совместно с кем-либо или над несколькими проектами одновременно, они становятся весьма удобными. Например, ваши коллеги могут распечатать файл <code>junk</code> с помощью команды</p>
    <p><code>$ cat /usr/you/junk</code></p>
    <p>Соответственно вы можете выяснить, какие файлы есть у <code>mary</code>:</p>
    <p><code>$ ls /usr/mary</code></p>
    <p><code>data</code></p>
    <p><code>junk</code></p>
    <p><code>$</code></p>
    <p>или скопировать один из ее файлов:</p>
    <p><code>$ cp /usr/mary/data data</code></p>
    <p>Вы можете редактировать ее файл:</p>
    <p><code>$ ed /usr/mary/data</code></p>
    <p>Если Мэри не хочет, чтобы вы имели доступ к ее файлам, или того же не хотите вы, можно обеспечить защищенность каждого файла и каталога. С этой целью предусмотрены права доступа на чтение-запись-выполнение для владельца, группы и всех остальных пользователей. Право доступа используется для контроля обращения к файлу или каталогу. (Вспомните результат <code>ls -l</code>.) В нашей системе большинство пользователей, как правило, считают, что открытая система более полезна, чем защищенная, но ваша политика может быть иной; мы вернемся к этому вопросу в гл. 2.</p>
    <p>Завершая серию экспериментов с абсолютными именами, попробуйте ввести</p>
    <p><code>$ ls /bin /usr/bin</code></p>
    <p>Не кажутся ли имена вам знакомыми? Когда вы запускаете команду, задавая ее после приглашения, система ищет файл с указанным именем. Вначале поиск ведется в вашем рабочем каталоге (где его, вероятно, найти не удается), затем в каталоге <code>/bin</code> и, наконец, в <code>/usr/bin</code>. Нет ничего особенного в командах, подобных <code>cat</code> или <code>ls</code>, за исключением того, что для удобства поиска и управления они находятся в нескольких каталогах. Чтобы убедиться в этом, попытайтесь выполнить некоторые из них, используя абсолютные имена:</p>
    <p><code>$ /bin/date</code></p>
    <p><code>Mon Sep 26 23:39:32 EDT 1983</code></p>
    <p><code>$ /bin/who</code></p>
    <p><code>srm tty1 Sep 26 22:20</code></p>
    <p><code>cvw tty4 Sep 26 22:40</code></p>
    <p><code>you tty5 Sep 26 23:04</code></p>
    <p><code>$</code></p>
    <cite>
     <subtitle>Упражнение 1.3</subtitle>
     <p>Попробуйте выполнить команду</p>
     <p><code>$ ls /usr/games</code></p>
     <p>а затем что-либо из предложенного ею. Большее удовольствие это доставит вам в нерабочее время.</p>
    </cite>
    <subtitle>Смена каталога. Команда <code>cd</code></subtitle>
    <p>Если вы постоянно работаете с информацией, хранящейся в каталоге <code>mary</code>, у вас может возникнуть желание работать с файлами Мэри, а не со своими. Для этого вам достаточно сменить каталог с помощью команды <code>cd</code>:</p>
    <p><code>$ cd /usr/mary</code></p>
    <p>Теперь, если использовать имя файла (без <code>/</code>) в качестве аргумента для команд <code>cat</code> или <code>pr</code>, это будет файл из каталога <code>mary</code>. Смена каталога не влияет на права доступа к файлу: если файл был недоступен из вашего каталога, то таким он и останется.</p>
    <p>Обычно бывает удобно сгруппировать свои файлы так, чтобы все файлы, относящиеся к одному проекту, попали в отдельный каталог. Например, если вы надумаете писать книгу, то весь текст вы, естественно, захотите хранить в каталоге с именем <code>book</code> (книга). Команда <code>mkdir</code> создает новый каталог:</p>
    <p><code>$ mkdir book </code>Создать каталог</p>
    <p><code>$ cd book    </code>Перейти в него</p>
    <p><code>$ pwd        </code>Убедиться, что вы попали куда надо</p>
    <p><code>/usr/you/book</code></p>
    <p><code>...          </code>Работа над книгой (прошло несколько минут)</p>
    <p><code>$ cd ..      </code>Подняться на один уровень в файловой системе</p>
    <p><code>$ pwd</code></p>
    <p><code>/usr/you</code></p>
    <p><code>$</code></p>
    <p>Обозначение <code>'..'</code> относится к "родителю" того каталога, с которым вы работаете в данный момент, а именно к каталогу, расположенному на один уровень ближе к корню. Обозначение <code>'.'</code> является синонимом текущего каталога.</p>
    <p><code>$ cd</code> Возврат в личный каталог</p>
    <p>Команда вернет вас в ваш начальный каталог, т.е. в каталог, в который вы попадаете при входе в систему.</p>
    <p>После того как книга опубликована, можно почистить каталог. Чтобы удалить каталог <code>book</code>, удалите все содержащиеся в нем файлы (быстрый способ выполнения этой операции мы вскоре покажем), затем перейдите в родительский каталог для <code>book</code> и задайте команду</p>
    <p><code>$ rmdir book</code></p>
    <p>Команда <code>rmdir</code> удаляет только пустые каталоги.</p>
   </section>
   <section>
    <title>
     <p>1.4 Интерпретатор <code>shell</code></p>
    </title>
    <p>Когда система выдает приглашение <code>$</code> и вы вводите команды для выполнения, вы имеете дело не с ядром самой системы, а с неким посредником, называемым интерпретатором команд, или <code>shell</code>. Это обычная программа, подобная <code>date</code> или <code>who</code>, хотя она может делать удивительные вещи. Тот факт, что программа <code>shell</code> находится между вами и ядром, дает реальные выгоды, и некоторые из них мы вам укажем. Применение программы-посредника обеспечивает три главных преимущества:</p>
    <p>• сокращенные имена файлов: можно задать целое множество файлов в качестве аргументов команде, указав шаблон для имен: <code>shell</code> будет искать файлы, имена которых соответствуют заданному шаблону;</p>
    <p>• переключение ввода-вывода: вывод любой программы можно направить в файл, а не на терминал, ввод можно получать из файла, а не с терминала. Ввод и вывод можно даже передать другим программам;</p>
    <p>• создание собственной среды: можно определить свои собственные команды и правила сокращений.</p>
    <subtitle>Сокращенное имя файла</subtitle>
    <p>Начнем с шаблонов имен файлов. Допустим, вы вводите обширный документ, наподобие книги. Логически он разбивается на множество частей, аналогично главам и разделам. И физически его следует разбить на части, поскольку затруднительно редактировать большие файлы. В этом случае для печати всего текста нужно указать ряд файлов. У вас могут быть отдельные файлы для каждой главы с именами <code>ch1</code>, <code>ch2</code> и т.д. Если каждая глава разбита на разделы, вы можете создать файлы с именами</p>
    <p><code>ch.1</code></p>
    <p><code>ch.2</code></p>
    <p><code>ch.3</code></p>
    <p><code>...</code></p>
    <p><code>ch2.1</code></p>
    <p><code>ch2.2</code></p>
    <p><code>...</code></p>
    <p>что и используется в нашей книге. При систематизированном именовании можно указать с первого взгляда, в какой ряд файлов попадает данный файл. Как быть, если вы захотите напечатать книгу? Можно задать</p>
    <p><code>$ pr ch1.1 ch1.2 ch1.3...</code></p>
    <p>но вы быстро устанете вводить имена файлов и начнете делать ошибки. Именно здесь приходит на помощь сокращенное имя файла. Если задать</p>
    <p><code>$ pr ch*</code></p>
    <p>интерпретатор <code>shell</code> воспримет <code>*</code> как любую последовательность символов, поэтому <code>ch*</code> является шаблоном, под который подходят все имена файлов из текущего каталога, начинающиеся на <code>ch</code>. Интерпретатор <code>shell</code> создаст список в алфавитном порядке<a l:href="#n3" type="note">[3]</a> и передаст его программе pr. Команда pr никогда "не узнает" * ; выбор по шаблону, который shell производит в текущем каталоге, порождает список строк, передаваемых <code>pr</code>.</p>
    <p>Ключевой момент состоит в том, что способ сокращения имени файла — это не свойство программы <code>pr</code>, а возможность, реализуемая интерпретатором <code>shell</code>. Поэтому вы можете использовать ее для создания последовательности имен файлов в любой команде, например для подсчета числа слов первой главы:</p>
    <p><code>$ wc ch1.*</code></p>
    <p><code> 113   562  3200 ch1.0</code></p>
    <p><code> 935  4081 22435 ch1.1</code></p>
    <p><code> 974  4191 22756 ch1.2</code></p>
    <p><code> 378  1561  8481 ch1.3</code></p>
    <p><code>1293  5298 28841 ch1.4</code></p>
    <p><code>  33   194  1190 ch1.5</code></p>
    <p><code>  75   323  2030 ch1.6</code></p>
    <p><code>3801 16210 88930 total</code></p>
    <p><code>$</code></p>
    <p>Существует программа с именем <code>echo</code> ("эхо"), которая особенно ценна для экспериментов со "смыслом" сокращенных имен. Как вы смогли догадаться, <code>echo</code> лишь повторяет свои аргументы.</p>
    <p><code>$ echo hello world</code></p>
    <p><code>hello world</code></p>
    <p><code>$</code></p>
    <p>Но аргументы могут формироваться путем выбора по шаблону. Так, команда</p>
    <p><code>$ echo ch1.*</code></p>
    <p>перечисляет имена всех файлов в гл. 1,</p>
    <p><code>$ echo *</code></p>
    <p>перечисляет имена всех файлов текущего каталога в алфавитном порядке,</p>
    <p><code>$ pr *</code></p>
    <p>выводит на печать содержимое всех ваших файлов (в алфавитном порядке), а</p>
    <p><code>$ rm *</code></p>
    <p>удаляет все файлы текущего каталога. (Лучше быть абсолютно уверенным, что вы действительно этого хотите!)</p>
    <p>Символ <code>*</code> может встречаться не только в конце имени файла. Его можно использовать всюду и даже по нескольку раз. Поэтому</p>
    <p><code>$ rm *.save</code></p>
    <p>удалит все файлы, оканчивающиеся на <code>.save</code>.</p>
    <p>Заметьте, что все имена файлов выбираются в алфавитном порядке, который отличается от числового. Если в вашей книге 10 глав, порядок может быть не тем, на который вы рассчитываете, поскольку <code>ch10</code> идет перед <code>ch2</code>:</p>
    <p><code>$ echo *</code></p>
    <p><code>ch1.1 ch1.2 ... ch10.1 ch10.2 ... ch2.1 ch2.2 ...</code></p>
    <p>Символ <code>*</code> — не единственный способ задания шаблона для интерпретатора <code>shell</code>, хотя и наиболее часто используемый. Шаблон <code>[...]</code> задает любые символы из перечисленных внутри скобок. Несколько подряд следующих букв или цифр можно задать в сокращенном виде:</p>
    <p><code>$ pr ch[12346789]* </code>Печать глав 1,2,3,4,6,7,8,9, но не 5</p>
    <p><code>$ pr ch[1-46-9]*   </code>То же самое</p>
    <p><code>$ rm temp[a-z]     </code>Удалить все tempa, …, tempz</p>
    <p>Шаблон <code>?</code> задает любой одиночный символ:</p>
    <p><code>$ ls ?        </code>Список файлов с именем из одного символа</p>
    <p><code>$ ls -l ch?.1 </code>Список ch1.1 ch2.1 ch3.1 и т.д., но не ch10.1</p>
    <p><code>$ rm temp?    </code>Удалить все файлы temp1, …, tempa и т.д.</p>
    <p>Отметим, что шаблоны сопоставляются только с именами существующих файлов. В частности, нельзя создать новые имена файлов с помощью шаблонов. Например, если вы захотите расширить <code>ch</code> до <code>chapter</code> в каждом имени файла, то такой вариант вам не поможет:</p>
    <p><code>$ mv ch.* chapter.*</code> Не работает!</p>
    <p>поскольку <code>chapter.*</code> не соответствует ни одному из существующих имен файлов.</p>
    <p>Символы шаблонов, подобные <code>*</code>, могут использоваться в абсолютных именах наравне с обычными именами файлов; сопоставление происходит для каждого компонента абсолютного имени, содержащего специальный символ. Так, <code>/usr/mary/*</code> инициирует поиск файлов в <code>/usr/mary/</code>, a <code>/usr/*/calendar</code> порождает список абсолютных имен всех пользователей, работающих с каталогом <code>calendar</code>.</p>
    <p>Если вам когда-нибудь придется отказаться от специального назначения символов <code>*</code>, <code>?</code> и др., заключите весь аргумент в апострофы, например:</p>
    <p><code>$ ls '?'</code></p>
    <p>Можно также предварить специальный символ обратной дробной чертой:</p>
    <p><code>$ ls \?</code></p>
    <p>(Вспомните, что, поскольку <code>?</code> не является символом стирания или уничтожения, обратная дробная черта перед ним будет обрабатываться не ядром, а интерпретатором <code>shell</code>.) Использование кавычек подробно рассматривается в гл. 3.</p>
    <cite>
     <subtitle>Упражнение 1.4</subtitle>
     <p>В чем состоит различие между следующими командами:</p>
     <p><code>$ ls junk  $ echo junk</code></p>
     <p><code>$ ls /     $ echo /</code></p>
     <p><code>$ ls       $ echo</code></p>
     <p><code>$ ls *     $ echo *</code></p>
     <p><code>$ ls '*'   $ echo '*'</code></p>
    </cite>
    <subtitle>Переключение ввода-вывода</subtitle>
    <p>Большинство команд, которые мы рассматривали, производят вывод на терминал, некоторые из них, подобно редактору, осуществляют ввод с терминала. А теперь приведем почти универсальное правило: терминал может быть заменен для ввода, вывода или обеих операций на файл.</p>
    <p>Например,</p>
    <p><code>$ ls</code></p>
    <p>выдает список файлов на ваш терминал. Но если задать</p>
    <p><code>$ ls &gt; filelist</code></p>
    <p>то тот же список файлов помещается вместо этого в файл <code>filelist</code>. Символ <code>&gt;</code> означает, что выходной поток должен быть помещен в указанный далее файл, а не выведен на терминал. Файл будет создан, если он ранее не существовал, или будет заменено содержимое старого. На своем терминале вы ничего не получите. В качестве другого примера можно слить несколько файлов, "перехватив" выходной поток команды <code>cat</code> и направив его в файл:</p>
    <p><code>$ cat f1 f2 f3 &gt; temp</code></p>
    <p>Символ <code>&gt;&gt;</code> действует подобно <code>&gt;</code>, но указывает на необходимость добавить выходной поток к концу файла. Значит, команда</p>
    <p><code>$ cat f1 f2 f3 &gt;&gt; temp</code></p>
    <p>сольет содержимое <code>f1</code>, <code>f2</code>, <code>f3</code> и добавит результат в конец <code>temp</code>, вместо того чтобы затереть его старое содержимое. Так же как и для операции <code>&gt;</code>, если файл <code>temp</code> не существует, то он будет создан первоначально пустым.</p>
    <p>Аналогично символ <code>&lt;</code> означает, что входной поток программы берется из последующего файла, а не с терминала. Так, можно заготовить письмо в файле <code>let</code>, а затем послать его нескольким адресатам:</p>
    <p><code>$ mail mary joe torn bob &lt; let</code></p>
    <p>Во всех этих примерах наличие пробелов по обе стороны символа <code>&gt;</code> или <code>&lt;</code> не обязательно, но такое представление традиционно.</p>
    <p>Имея возможность переключать выходной поток с помощью <code>&lt;</code>, мы можем комбинировать команды, получая эффект, недостижимый другим способом. Например, можно выдать список пользователей в алфавитном порядке</p>
    <p><code>$ who &gt; temp</code></p>
    <p><code>$ sort &lt; temp</code></p>
    <p>Поскольку команда <code>who</code> выдает по одной строке на каждого пользователя, работающего в системе, a <code>wc -l</code> производит подсчет строк (подавляя вывод числа слов и символов), можно подсчитать число пользователей с помощью команд:</p>
    <p><code>$ who &gt; temp</code></p>
    <p><code>$ wc -l &lt; temp</code></p>
    <p>и число файлов в текущем каталоге:</p>
    <p><code>$ ls &gt; temp</code></p>
    <p><code>$ wc -l &lt; temp</code></p>
    <p>хотя в это число войдет и сам файл <code>temp</code>. Можно выдать список имен файлов в три столбца, задав</p>
    <p><code>$ ls &gt; temp</code></p>
    <p><code>$ pr -3 &lt; temp</code></p>
    <p>Наконец, можно убедиться в том, что некий пользователь вошел в систему, комбинируя команды <code>who</code> и <code>grep</code>:</p>
    <p><code>$ who &gt; temp</code></p>
    <p><code>$ grep mary &lt; temp</code></p>
    <p>Во всех перечисленных выше примерах, как и в случае имен файлов, содержащих образы типа <code>*</code>, важно понимать, что символы <code>&lt;</code> и <code>&gt;</code> обрабатываются самим интерпретатором <code>shell</code>, а не отдельной программой. Благодаря этому переключение входного и выходного потоков возможно для любой программы, причем сама программа даже "не подозревает", что происходит что-то необычное.</p>
    <p>Изложенное подводит нас к важному выводу. Команда</p>
    <p><code>$ sort &lt; temp</code></p>
    <p>сортирует содержимое файла <code>temp</code> так же, как</p>
    <p><code>$ sort temp</code></p>
    <p>но в их действиях есть различие. Поскольку строка <code>&lt; temp</code> обрабатывается интерпретатором <code>shell</code>, первая команда <code>sort</code> не воспринимает файл <code>temp</code> как свой аргумент; она просто сортирует собственный стандартный входной поток, который переключен интерпретатором на файл <code>temp</code>. В то же время в последнем случае имя temp передается команде sort в качестве аргумента, она читает его и сортирует файл. Команде sort можно передать список файлов:</p>
    <p><code>$ sort temp1 temp2 temp3</code></p>
    <p>но, если имена файлов отсутствуют, она всегда будет сортировать стандартный входной поток. Это существенная особенность большинства команд: если не указаны имена файлов, то обрабатывается стандартный входной поток. Следовательно, достаточно ввести имя команды, чтобы посмотреть, как она выполняется. Например,</p>
    <p><code>$ sort</code></p>
    <p><code>ghi</code></p>
    <p><code>abc</code></p>
    <p><code>def</code></p>
    <p><code>ctl-c</code></p>
    <p><code>abc</code></p>
    <p><code>def</code></p>
    <p><code>ghi</code></p>
    <p><code>$</code></p>
    <p>В дальнейшем мы покажем, как реализуется этот принцип.</p>
    <cite>
     <subtitle>Упражнение 1.5</subtitle>
     <p>Объясните, почему команда</p>
     <p><code>$ ls &gt; ls.out</code></p>
     <p>включает <code>ls.out</code> в список имен.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 1.6</subtitle>
     <p>Объясните результат выполнения команды</p>
     <p><code>$ wc temp &gt; temp</code></p>
     <p>Что произойдет, если вы ошибетесь в имени команды, задав</p>
     <p><code>$ woh &gt; temp</code></p>
    </cite>
    <subtitle>Программные каналы</subtitle>
    <p>Все примеры, приведенные в конце предыдущего раздела, основаны на одном и том же приеме: выходной поток одной программы передается в качестве входного потока для другой программы через временный файл. Сам временный файл больше не имеет никакого смысла; в самом деле, неудобно использовать такой файл. Это соображение привело к возникновению одной из фундаментальных концепций системы UNIX, идеи <emphasis>программного канала</emphasis>. Программный канал представляет собой средство связи выходного потока одной программы с входным потоком другой без всяких временных файлов; соединение программным каналом двух или более программ называется <emphasis>конвейером</emphasis>.</p>
    <p>Пересмотрим теперь некоторые из предыдущих примеров с точки зрения использования программных каналов вместо временных файлов. Вертикальная черта служит указанием интерпретатору <code>shell</code> для создания конвейера:</p>
    <p><code>$ who | sort      </code>Печать отсортированного списка пользователей</p>
    <p><code>$ who | wc -l     </code>Подсчет числа пользователей</p>
    <p><code>$ ls | wc -l      </code>Подсчет числа файлов</p>
    <p><code>$ ls | pr -3      </code>Вывод списка имен файлов в три столбца</p>
    <p><code>$ who | grep mary </code>Поиск определенного пользователя</p>
    <p>Всякая программа, вводящая информацию с терминала, может вводить ее и по программному каналу; всякая программа, производящая вывод на терминал, может выдавать информацию в программный канал. Это тот случай, когда приносит плоды решение читать стандартный входной поток, если не заданы никакие файлы. Любая программа, выполняющая данное соглашение, может быть включена в конвейер. В рассмотренных выше примерах команды <code>pr</code>, <code>grep</code>, <code>sort</code> и <code>wc</code> используются именно таким способом.</p>
    <p>Можно связать конвейером сколь угодно много программ. Например,</p>
    <p><code>$ ls | pr -3 | lpr</code></p>
    <p>создает список имен файлов в три столбца и выдает его на печатающее устройство, а</p>
    <p><code>$ who | grep mary | wc -l</code></p>
    <p>подсчитывает, сколько раз пользователь Мэри входила в систему.</p>
    <p>Программы, связанные конвейером, выполняются одновременно, а не последовательно одна за другой. Это означает, что программы в конвейере могут вступать в диалог; ядро выполняет необходимые операции переключения и синхронизации, чтобы такая схема работала. Большинство команд следует определенному образцу, поэтому они хорошо вписываются в конвейер и могут выполняться в нем на любом месте. Обычный вызов команды имеет вид:</p>
    <p><code><emphasis>команда флаги возможные имена файлов</emphasis></code></p>
    <p>Если имена файлов не указаны, то команда читает стандартный входной поток, который по умолчанию поступает с терминала (что удобно для экспериментирования), однако возможно его переключение на файл или программный канал. Кроме того, во многих командах выдача идет в стандартный выходной поток, который по умолчанию направлен на терминал, но его также можно переключить на файл или программный канал.</p>
    <p>Сообщения же об ошибках, выдаваемые командами, следует обрабатывать по-другому, иначе они затеряются в файле или программном канале. Поэтому каждая команда имеет еще один стандартный файл, называемый файлом диагностики, который обычно связан с вашим терминалом:</p>
    <image l:href="#img_2.jpeg"/>
    <p><emphasis>Рис. 1.2</emphasis>: Схема потоков в UNIX</p>
    <empty-line/>
    <p>Почти все рассматривавшиеся выше команды укладываются в эту схему; исключение составляют <code>who</code> и <code>date</code>, не имеющие входной информации, а также те, например <code>cmp</code> или <code>diff</code>, которые имеют определенное число входных файлов. (Посмотрите их флаг <code>'-'</code>.)</p>
    <cite>
     <subtitle>Упражнение 1.7</subtitle>
     <p>Объясните разницу между командами</p>
     <p><code>$ who | sort</code></p>
     <p>и</p>
     <p><code>$ who &gt; sort</code></p>
    </cite>
    <subtitle>Процессы</subtitle>
    <p>Интерпретатор <code>shell</code> выполняет и некоторые другие операции, помимо связывания через программный канал. Рассмотрим кратко вопрос одновременного выполнения нескольких программ, о чем уже упоминалось при обсуждении программных каналов. Например, можно запустить две команды с помощью одной командной строки, разделив их точкой с запятой; интерпретатор <code>shell</code> распознает этот символ и разобьет строку на две команды:</p>
    <p><code>$ date; who</code></p>
    <p><code>Tue Sep 27 01:03:17 EDT 1983</code></p>
    <p><code>ken tty0 Sep 27 00:43</code></p>
    <p><code>dmr tty1 Sep 26 23:45</code></p>
    <p><code>rob tty2 Sep 26 23:59</code></p>
    <p><code>bwk tty3 Sep 27 00:06</code></p>
    <p><code>jj  tty4 Sep 26 23:31</code></p>
    <p><code>you tty5 Sep 26 23:04</code></p>
    <p><code>her tty7 Sep 26 23:34</code></p>
    <p>Обе команды будут выполнены (подряд) прежде, чем интерпретатор вновь вернется с приглашением.</p>
    <p>Можно также при желании запустить несколько команд одновременно. Предположим, что вы собираетесь заняться длительными вычислениями, например, подсчитать число слов в вашей книге, но не хотите ждать окончания команды <code>wc</code> для перехода к другой работе. Тогда можно задать:</p>
    <p><code>$ wc ch* &gt; wc.out &amp;</code></p>
    <p><code>6944</code> Shell дает номер процесса</p>
    <p><code>$</code></p>
    <p>Амперсанд (<code>&amp;</code>) в конце командной строки указывает интерпретатору, что нужно запустить данную команду, а затем сразу перейти к получению последующих команд с терминала, т.е. не ждать ее завершения. Итак, команда будет выполняться, а вы можете отвлечься на что-нибудь другое. Переключение выходного потока на файл <code>wc.out</code> предотвращает возможность его смешивания с той информацией, которая появится на терминале в процессе дальнейшей работы.</p>
    <p>Каждый экземпляр запущенной программы называется <emphasis>процессом</emphasis>. Число, выдаваемое <code>shell</code> в ответ на команду, введенную с <code>&amp;</code>, является номером процесса. Его можно использовать в других командах в качестве ссылки на данный экземпляр выполняемой программы.</p>
    <p>Важно понимать различие между программами и процессами. Скажем, <code>wc</code> — это программа, но каждый запуск программы <code>wc</code> создает новый процесс. Если одновременно выполняется несколько экземпляров одной программы, то любой из них считается отдельным процессом с отличным от других номером.</p>
    <p>Если конвейер завершается операцией <code>&amp;</code></p>
    <p><code>$ pr ch * | lpr &amp;</code></p>
    <p><code>6951</code> Номер процесса</p>
    <p><code>$</code></p>
    <p>то все процессы этого конвейера начинают выполняться сразу, и <code>&amp;</code> относится ко всем программам, участвующим в конвейере. Однако выдается только номер процесса, относящийся к последней программе в конвейере. Команда</p>
    <p><code>$ wait</code></p>
    <p>ожидает, пока не завершатся все процессы, запущенные с помощью <code>&amp;</code>. Если она не возвращается сразу, значит, у вас есть незавершенные команды. Прервать выполнение команд можно, нажав клавишу <emphasis>DELETE</emphasis>.</p>
    <p>Можно использовать номер процесса, сообщаемый интерпретатором, для остановки процесса, инициированного операцией <code>&amp;</code>:</p>
    <p><code>$ kill 6944</code></p>
    <p>Если вы забыли номер процесса, команда <code>ps</code> выведет сообщение обо всех ваших процессах. В том случае, когда вам некогда, команда <code>kill 0</code> уничтожит все ваши процессы, за исключением начального процесса-интерпретатора. Если же вам интересно, что делают другие пользователи, команда <code>ps -ag</code> сообщит обо всех выполняемых процессах. Приведем пример вывода:</p>
    <p><code>$ ps -ag</code></p>
    <p><code> PID TTY TIME CMD</code></p>
    <p><code>  36 со 6:29 /etc/cron</code></p>
    <p><code>6423 5  0:02 -sh</code></p>
    <p><code>6704 1  0:04 -sh</code></p>
    <p><code>6722 1  0:12 vi paper</code></p>
    <p><code>4430 2  0:03 -sh</code></p>
    <p><code>6612 7  0:03 -sh</code></p>
    <p><code>6628 7  1:13 rogue</code></p>
    <p><code>6643 2  0:02 write dmr 6949 4 0:01 login bimmler</code></p>
    <p><code>6952 5  0:08 pr ch1.1 ch1.2 ch1.3 ch1.4</code></p>
    <p><code>6951 5  0:03 lpr</code></p>
    <p><code>6959 5  0:02 ps -ag</code></p>
    <p><code>6844 1  0:02 write rob</code></p>
    <p><code>$</code></p>
    <p>Здесь <code>PID</code> — номер процесса; <code>TTY</code> — терминал, связанный с процессом (как в команде <code>who</code>); <code>TIME</code> — затраченное время процессора в минутах и секундах, а в конце строки — выполняемая команда. Команда <code>ps</code> — одна из тех команд, которые выполняются по- разному в различных версиях системы, так что вывод в вашей системе может иметь другой формат. Даже аргументы могут отличаться — см. в своем справочном руководстве страницу <code>ps(1)</code>.</p>
    <p>Процессы, подобно файлам, имеют иерархическую структуру: у каждого процесса есть родитель и могут быть потомки. Ваша копия интерпретатора <code>shell</code> была создана процессом, обслуживающим связь через терминал с системой. Когда вы запускаете команды, их процессы становятся прямыми потомками вашей копии <code>shell</code>. Если вы запускаете программу "внутри" одной из этих команд, например команду <code>'!'</code> для выхода из редактора <code>ed</code>, то создается новый процесс-потомок, который является, таким образом, уже внуком для <code>shell</code>.</p>
    <p>Иногда процесс выполняется столь долго, что вы уже жалеете, что запустили его. Выключите терминал и идите домой, не дожидаясь его окончания. Но если вы выключите терминал или отсоедините его от сети, то процесс будет уничтожен, даже если применен <code>&amp;</code>. Специально для такого случая предусмотрена команда <code>nohup</code> ("no hangup" — без отбоя).</p>
    <p>Введите</p>
    <p><code>$ nohup <emphasis>команда</emphasis> &amp;</code></p>
    <p>и команда будет продолжать выполняться, даже если выйти из системы. Любой результат выполнения команды будет сохранен в файле, называемом <code>nohup.out</code>. После запуска программы никакая команда <code>nohup</code> уже не поможет.</p>
    <p>Если ваш процесс требует много процессорного времени, вы можете облегчить участь тех, кто работает вместе с вами, запустив его с приоритетом ниже обычного. Это можно сделать с помощью программы <code>nice</code>:</p>
    <p><code>$ nice <emphasis>большая-команда</emphasis> &amp;</code></p>
    <p>Команда <code>nohup</code> автоматически вызывает <code>nice</code>, поскольку раз уж вы собираетесь выйти из системы, то можете позволить, чтобы ваша команда выполнялась дольше.</p>
    <p>Наконец, вы можете дать указание системе запустить ваш процесс в необычное время, скажем, утром, когда все нормальные люди снят, а не работают на машине. Команда называется <code>at(1)</code>:</p>
    <p><code>$ at</code></p>
    <p><code>время любые команды</code></p>
    <p><code>какие угодно...</code></p>
    <p><code>ctl-d</code></p>
    <p><code>$</code></p>
    <p>Это пример типичного использования команды <code>at</code>, но, конечно, команды можно брать и из файла:</p>
    <p><code>$ at 3am &lt; файл</code></p>
    <p><code>$</code></p>
    <p>Время можно задавать исходя из 24-часового цикла как <code>2130</code> или 12-часового как <code>930pm</code>.</p>
    <subtitle>Создание среды</subtitle>
    <p>Одним из достоинств системы UNIX является то, что вы можете легко адаптировать ее по своему вкусу либо в соответствии с местными традициями программистского мира. Например, как отмечалось выше, существуют разные стандарты для символов стирания и удаления; по умолчанию используются <code>#</code> и <code>@</code> . Вы можете изменить их в любой момент с помощью команды</p>
    <p><code>$ stty erase е kill k</code></p>
    <p>где <code>'e'</code> обозначает нужный вам символ стирания, а <code>'k'</code> — символ удаления. Однако задавать эти символы при каждом входе в систему — довольно нудное занятие.</p>
    <p>На помощь здесь приходит <code>shell</code>. Если в вашем начальном каталоге есть файл <code>.profile</code>, интерпретатор будет запускать команды из него при каждом входе в систему перед выдачей первого приглашения. Поэтому можно поместить команды в <code>.profile</code> для установки требуемой среды, и они будут выполняться всякий раз при входе в систему.</p>
    <p>Большинство пользователей первым делом помещают в свой файл <code>.profile</code> команду</p>
    <p><code>$ stty erase &#8592;</code></p>
    <p>Мы использовали &#8592;, чтобы сделать символ стирания видимым, но вы должны поместить в <code>.profile</code> настоящий символ "шаг назад". Команда <code>stty</code> воспринимает также обозначение <code>^x</code> в качестве <code>ctl-x</code>, поэтому тот же результат можно получить, вводя:</p>
    <p><code>$ stty erase '^h'</code></p>
    <p>поскольку <emphasis>ctl-h</emphasis> и есть шаг назад. (Символ <code>'^'</code> ранее применялся для операции программного канала <code>|</code>, поэтому его следует экранировать с помощью кавычек.) Если на вашем терминале нет возможности задать интервалы табуляции, можно добавить к строке с <code>stty</code> аргумент <code>-tabs</code>:</p>
    <p><code>stty erase '^h' -tabs</code></p>
    <p>Если у вас есть желание посмотреть, как загружена система, при входе в нее добавьте для подсчета пользователей конвейер</p>
    <p><code>who | wc -l</code></p>
    <p>Если имеется служба новостей, можно добавить команду <code>news</code>. Те, кому нравится игра <code>fortune</code>, могут добавить</p>
    <p><code>/usr/games/fortune</code></p>
    <p>Спустя некоторое время вы обнаружите, что период входа в систему существенно возрос, и выберете для себя оптимальный вариант.</p>
    <p>Некоторыми возможностями системы можно управлять с помощью так называемых shell-переменных, значения которых пользователь может и посмотреть, и установить. Например, строка-приглашение, обозначаемая ранее как <code>$</code>, на самом деле хранится в shell-переменной, называемой <code>PS1</code>, и можно присвоить ей любое значение:</p>
    <p><code>PS1='Yes, dear ?'</code> Да, дорогой?</p>
    <p>Кавычки необходимы, поскольку в строке-приглашении есть пробелы, а в этой операции пробелы перед и после <code>"="</code> не допускаются.</p>
    <p>Интерпретатор также выделяет переменные <code>НОМЕ</code> и <code>MAIL</code>. <code>НОМЕ</code> представляет собой имя вашего начального каталога; переменная обычно имеет правильное значение даже без установки ее в <code>.profile</code>. Переменная <code>MAIL</code> содержит имя стандартного файла, в котором хранится ваша почта. Если вы переопределите ее для интерпретатора, то будете в случае появления новой почты получать извещение после ввода каждой команды.<a l:href="#n4" type="note">[4]</a></p>
    <p><code>MAIL=/usr/spool/mail/you</code></p>
    <p>(В вашей системе файл для почты может быть другим; распространенным является и имя <code>/usr/mail/you</code>.)</p>
    <p>Наиболее полезной переменной интерпретатора <code>shell</code>, вероятно, считается та, которая определяет, где проводится поиск команд. Вспомните, что, когда вы вводите имя команды, интерпретатор обычно вначале ищет его в текущем каталоге, затем в <code>/bin</code> и далее в <code>/usr/bin</code>. Эта последовательность каталогов называется <emphasis>путем поиска</emphasis> и хранится в переменной интерпретатора с именем <code>PATH</code>. Если определенный по умолчанию путь поиска вас не устраивает, то его можно изменить (опять в файле <code>.profile</code>). Например, строкой ниже к стандартному пути поиска добавляется <code>/usr/games</code>:</p>
    <p><code>PATH=.:/bin:/usr/bin:/usr/games/</code> Один способ…</p>
    <p>Синтаксис может показаться вам несколько странным: последовательность имен каталогов разделяется двоеточием. Напоминаем, что обозначает текущий каталог. Можно опустить имя <code>'.'</code>, пустой компонент в <code>PATH</code> обозначает текущий каталог.</p>
    <p>Другой способ установить значение <code>PATH</code> — просто добавить к предыдущему значению</p>
    <p><code>PATH=$PATH:/usr/games</code> … Другой способ</p>
    <p>Можно получить значение любой переменной интерпретатора, предварив ее имя символом <code>$</code>. В приведенном примере выражение <code>$PATH</code> выбирает текущее значение, к которому добавляется новый компонент, и результат вновь присваивается <code>PATH</code>. Можно проверить это с помощью команды <code>echo</code>:</p>
    <p><code>$ echo PATH is $PATH</code></p>
    <p><code>PATH is :/bin:/usr/bin:/usr/games</code></p>
    <p><code>$ echo $HOME</code> Ваш начальный каталог</p>
    <p><code>/usr/you</code></p>
    <p><code>$</code></p>
    <p>Если у вас есть свои собственные команды, то, возможно, вы захотите собрать их в свой каталог и добавить его к пути поиска. В таком случае переменная <code>PATH</code> может принять подобное значение:</p>
    <p><code>PATH=:$HOME/bin:/bin:/usr/bin:/usr/games</code></p>
    <p>Вопрос создания своих собственных команд мы обсудим в гл. 3.</p>
    <p>Существует еще одна переменная, часто используемая текстовыми редакторами, более популярными, чем <code>ed</code>, — <code>TERM</code>, которая указывает тип используемого терминала. Эта информация позволяет программам более эффективно работать с экраном. Поэтому можно в <code>.profile</code> добавить, например, следующее:</p>
    <p><code>TERM=adm3</code></p>
    <p>Можно применять переменные и для сокращения записи. Если вам часто приходится ссылаться на каталог с длинным именем, имеет смысл добавить строку типа</p>
    <p><code>d=/horribly/long/directory/name</code></p>
    <p>к файлу <code>.profile</code>, чтобы использовать:</p>
    <p><code>$ cd $d</code></p>
    <p>Ваши собственные переменные, скажем <code>d</code>, по традиции обозначаются строчными буквами, что позволяет отличить их от тех, которые, как <code>PATH</code>, использует сам интерпретатор.</p>
    <p>Наконец, вы должны сообщить интерпретатору, что будете использовать некоторые переменные в других программах; для этого служит команда <code>export</code>, к которой мы вернемся в гл. 3:</p>
    <p><code>export MAIL PATH TERM</code></p>
    <p>Подводя итоги, покажем, как может выглядеть типичный файл <code>.profile</code>:</p>
    <p><code>$ cat .profile</code></p>
    <p><code>stty erase '^h' -tabs</code></p>
    <p><code>MAIL=/usr/spool/mail/you</code></p>
    <p><code>PATH=:$HOME:/bin:/usr/bin:/usr/games</code></p>
    <p><code>TERM=adm3</code></p>
    <p><code>b=$HOME/book</code></p>
    <p><code>export MAIL PATH TERM b</code></p>
    <p><code>date</code></p>
    <p><code>who | wc -l $</code></p>
    <p>Мы еще далеко не полностью перечислили возможности интерпретатора. В частности, можно создать собственные команды, собрав в файле уже существующие команды, и в данном случае уже будут интерпретироваться команды из этого файла. Удивительно, как многого можно добиться с помощью такого в основе своей простого средства. Мы рассмотрим его в гл. 3.</p>
   </section>
   <section>
    <title>
     <p>1.5 Другие средства UNIX</p>
    </title>
    <p>С системой UNIX связано значительно больше проблем, чем те, которые мы обсудили в настоящей главе. Многие из них еще будут рассматриваться в этой книге. Теперь вы должны чувствовать себя увереннее в общении с системой и, в частности, уметь ориентироваться в ее справочном руководстве. Если у вас появятся вопросы, в каких случаях и как использовать команды, всегда обращайтесь к руководству.</p>
    <p>Имеет смысл также периодически заглядывать в руководство, чтобы освежить свои знания об известных вам командах и познакомиться с новыми. В руководстве описывается множество программ, которые мы не обсуждали, включая компиляторы языков программирования, подобные Фортран 77, программы-калькуляторы типа <code>bc(1)</code>, <code>cu(1)</code> и <code>uucp(1)</code> — программы для межмашинного взаимодействия, графические пакеты, статистические программы и даже такая программа, как <code>units(1)</code>.</p>
    <p>Как уже отмечалось ранее, эта книга не заменяет справочное руководство, а дополняет его. В последующих главах мы будем изучать фрагменты и программы системы UNIX, используя информацию, приведенную в руководстве, и следуя логике взаимодействия компонентов системы. Хотя это взаимодействие явно не описано в руководстве, именно оно лежит в основе приемов программирования в системе.</p>
    <cite>
     <subtitle>Историческая и библиографическая справка</subtitle>
     <p>Первой публикацией по системе UNIX является статья Д. М. Ритчи и К. Л. Томпсона "The UNIX Time-sharing System" (Communications of the ACM, July, 1974). Она была перепечатана там же в январе 1983 г. (стр. 89 из перепечатки есть в мартовском выпуске 1983 г.). Это обзор системы для специалистов по операционным системам, но мы рекомендуем познакомиться с ним всем программистам.</p>
     <p>Специальный июльский выпуск журнала The Bell System Technical Journal (BSTJ) 1978 г. содержит ряд статей, посвященных дальнейшему развитию системы и некоторым историческим вопросам, включая переработанный вариант статьи Ритчи и Томпсона. Следующий специальный выпуск BSTJ, содержащий новые статьи по системе UNIX, вышел в свет в 1984 г.</p>
     <p>В статье Б. Кернигана и Д. Мэши "The UNIX Programming Environment" (IEEE Computer Magazine, April, 1981) делается попытка выделить наиболее существенные свойства системы с точки зрения программистов.</p>
     <p>В справочном руководстве по системе UNIX, какой бы ни была ваша версия системы, вы найдете команды, системные функции и правила взаимодействия с ними, форматы файлов и процедуры поддержания системы. Вы не сможете обойтись без этого руководства, хотя на первых порах, пока вы не начнете программировать, вам, вероятно, будет достаточно прочесть только часть первого тома. Том 1 справочного руководства по седьмой версии системы опубликован издательством Холта, Райнхарта и Уинстона. Том 2 "Documents for Use with the UNIX Timesharing System" справочного руководства содержит рекомендации по использованию и описания большинства команд. В частности, здесь описываются достаточно подробно средства подготовки документации и разработки программ. В конечном счете, мы уверены, вас заинтересует этот материал.</p>
     <p>Хорошим введением для совсем "зеленых" новичков и непрограммистов представляется книга Э. и Н. Ломато "A UNIX Primer" (Prentice-Hall, 1983).</p>
    </cite>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 2</p>
    <p>Файловая система</p>
   </title>
   <section>
    <p>Все, с чем работает система UNIX, она воспринимает в виде файла. Это не такое уж упрощение, как может показаться на первый взгляд. Когда разрабатывалась первая версия системы, даже прежде, чем ей дали имя, все усилия сосредоточились на создании структуры файловой системы, которая должна была быть простой и удобной в использовании. Файловая система — ключевое звено, обеспечившее успешное применение UNIX. Это наилучший пример философии "прекрасное в малом", показывающий, какой мощи можно достичь реализацией нескольких хорошо продуманных идей.</p>
    <p>Для описания команд и их взаимодействия нужно хорошо знать структуру и внешние связи файловой системы. В этой главе излагается большинство вопросов, связанных с файловой системой, — понятие файла и его представление, каталоги и иерархия файловой системы, права доступа, индексный дескриптор (внутреннее представление файла в системе) и файлы устройств. Поскольку основная работа в системе связана с манипулированием файлами, существует множество команд для анализа и модификации файла; здесь вводятся наиболее употребительные команды.</p>
   </section>
   <section>
    <title>
     <p>2.1 Основные сведения о файлах</p>
    </title>
    <p>Файл представляет собой последовательность байтов. (Байт — небольшая порция информации, обычно размером в восемь бит. Для наших целей можно считать байт синонимом слова "символ".) Никаких ограничений по структуре системой на файл не накладывается, и никакого смысла не приписывается его содержимому: смысл байтов зависит исключительно от программ, обрабатывающих файл. Более того, как мы увидим позднее, это верно не только для файлов, хранящихся на дисках, но и для файлов, представляющих периферийные устройства. Записи на магнитных лентах, почта, символы, вводимые с клавиатуры, вывод на печатающее устройство, данные, передаваемые по конвейеру — каждый из этих файлов система и входящие в нее программы воспринимают просто как последовательность байтов.</p>
    <p>Лучше всего познакомиться с файлами экспериментальным путем, так что начнем с создания небольшого файла:</p>
    <p><code>$ ed а</code></p>
    <p><code>now is the time,</code></p>
    <p><code>for all good people</code></p>
    <p><code>.</code></p>
    <p><code>w junk</code></p>
    <p><code>36</code></p>
    <p><code>q</code></p>
    <p><code>$ls -l</code></p>
    <p><code>-rw-r--r-- 1 you 26 Sep 27 06:11 junk</code></p>
    <p><code>$</code></p>
    <p>Здесь <code>junk</code> — это файл из 36 байт, т.е. 36 символов, которые вы ввели (не считая, конечно, символов, введенных при коррекции ошибок). Команда <code>cat</code> показывает содержимое файла в следующем виде:</p>
    <p><code>$ cat junk</code></p>
    <p><code>now is the time</code></p>
    <p><code>for all good people</code></p>
    <p><code>$</code></p>
    <p>Команда <code>od</code> ("octal dump" — восьмеричный дамп) выдает "изображение" всех байтов файла:</p>
    <p><code>$ od -с junk</code></p>
    <p><code>0000000 n o w   i s   t h e   t i m e \n</code></p>
    <p><code>0000020 f o r   a l l   g o o d   p e o</code></p>
    <p><code>0000040 p l e \n</code></p>
    <p><code>0000044</code></p>
    <p><code>$</code></p>
    <p>Флаг <code>-с</code> означает, что следует интерпретировать байты как символы. Если добавить флаг <code>-b</code>, то можно, кроме того, показать байты как восьмеричные числа.<a l:href="#n5" type="note">[5]</a></p>
    <p><code>$ od -cb junk</code></p>
    <p><code>0000000  n   o   w       i   s       t   h   e       t   i   m   e  \n</code></p>
    <p><code>        156 157 167 040 151 163 040 164 150 145 040 164 151 155 145 012</code></p>
    <p><code>0000020  f   o   r       a   l   l       g   o   o   d       p   e   o</code></p>
    <p><code>        146 157 162 040 141 154 154 040 147 157 157 144 040 160 145 157</code></p>
    <p><code>0000040  d   l   e  \n</code></p>
    <p><code>        160 154 145 012</code></p>
    <p><code>0000044 $</code></p>
    <p>Семизначные числа в колонке слева показывают место в файле, т.е. порядковый номер следующего изображаемого символа в восьмеричной форме. Между прочим, приоритет восьмеричных чисел — это пережиток времен PDP-11, когда восьмеричной нотации отдавалось предпочтение. Для других машин больше подходит шестнадцатеричная нотация; флаг <code>-х</code> предписывает команде <code>od</code> печатать информацию в шестнадцатеричной форме.</p>
    <p>Обратите внимание на то, что после каждой строки идет символ с восьмеричным значением 012. Это символ перевода строки для ASCII; система помещает его во входной поток, когда вы нажимаете клавишу <emphasis>RETURN</emphasis>. По соглашению, заимствованному из языка Си, символ перевода строки изображается как <code>\n</code>, что лишь облегчает чтение. Такого соглашения придерживаются только программы типа <code>od</code>; в файле же хранится единственный байт 012.</p>
    <p>Перевод строки — наиболее типичный пример специального символа. Другими специальными символами, связанными с некоторыми операциями управления терминалом, являются символы: шаг назад (восьмеричное значение 010 изображается как <code>\b</code>), табуляция (011, <code>\t</code>), возврат каретки (015, <code>\r</code>).</p>
    <p>Важно в каждом случае различать, в каком виде символ хранится в файле и как он интерпретируется в той или иной ситуации. Например, когда вы вводите с клавиатуры символ "шаг назад" (предполагая, что это ваш символ стирания), система воспринимает его как требование уничтожить символ, введенный перед ним. Оба символа — и стираемый, и "шаг назад" — на терминале исчезают, а курсор возвращается на одну позицию назад.</p>
    <p>Если ввести последовательность</p>
    <p><code>\&#8592;</code></p>
    <p>(т.е. символ <code>\</code> и вслед за ним "шаг назад"), то ядро в этом случае "считает", что вы действительно хотите ввести символ <code>&#8592;</code>, поэтому <code>\</code> исчезает, а в вашем файле появляется байт 010. Когда "шаг назад" отражается на терминале, происходит возврат курсора, так что он указывает на символ <code>\</code>.</p>
    <p>При выводе файла, содержащего символ <code>&#8592;</code>, он передается на терминал без обработки, что опять приводит к передвижке курсора на одну позицию назад. Если воспользоваться командой <code>od</code>, чтобы вывести файл, содержащий символ <code>&#8592;</code>, он появится как байт со значением 010 или, если указан флаг <code>-с</code>, как <code>\b</code>.</p>
    <p>Аналогичную ситуацию мы имеем и с символом табуляции: при вводе он отражается на терминале и посылается программе, осуществляющей ввод; при выводе символ табуляции просто передается на терминал и интерпретируется. Однако в отличие от предыдущего случая здесь можно указать ядру, что вы хотите получить интерпретацию табуляции при выводе; тогда вместо изображения каждого символа табуляции будет выдаваться нужное число пробелов, чтобы перейти к следующей позиции табуляции. Позиции табуляции установлены в столбцах 9, 17, 25 и т.д. Команда</p>
    <p><code>$ stty = tabs</code></p>
    <p>приводит к замене символов табуляции пробелами при выводе на терминал см. описание <code>stty(1)</code>.</p>
    <p>Обработка символа <emphasis>RETURN</emphasis> аналогична рассмотренной выше. Ядро отображает <emphasis>RETURN</emphasis> на терминале как "возврат каретки" и "конец строки", но во входной поток попадает только "перевод строки". При выводе этот символ вновь заменяется символами возврата каретки и конца строки.</p>
    <p>Подход системы UNIX к представлению управляющей информации нетрадиционен, особенно использование символа перевода строки для завершения строки (в качестве конца посылки). Многие системы вместо этого трактуют каждую строку как "запись", содержащую не только введенные данные, но и счетчик числа символов в строке (специального символа конца строки нет). В других системах каждая строка завершается символами возврата каретки и перевода строки, поскольку такая последовательность необходима для вывода на большинство терминалов. (Слово "linefeed" завершение строки, синоним перевода строки, поэтому такую последовательность часто называют "CRLF", что невозможно произнести.)</p>
    <p>Система UNIX не делает ни того, ни другого: нет записей и счетчиков, к тому же ни в одном файле нет никаких байтов, которые бы вы или ваша программа не поместили туда. Символ перевода строки преобразуется в два символа возврата каретки и перевода строки при выводе на терминал, но программы должны иметь дело с одним символом перевода строки, поскольку это все, что они могут "увидеть". В большинстве случаев подобная простая схема является оптимальной. Если необходима более сложная структура, ее легко построить на базе этой, тогда как получить простое из сложного значительно трудней.</p>
    <p>Поскольку завершение строки обозначается символом перевода строки, можно ожидать, что и файл завершается другим специальным символом, скажем <code>\е</code> как сокращение "end of file" конец файла. Но, посмотрев на вывод программы <code>od</code>, вы не увидите никакого специального символа в конце файла он просто кончается. Вместо того чтобы использовать специальный символ, система отмечает конец файла сообщением о том, что данных в файле больше нет. Ядро запоминает длину файла, поэтому программа встречает конец файла после обработки всех составляющих файл байтов.</p>
    <p>Программы выбирают данные из файла с помощью системного обращения с именем <code>read</code> (подпрограмма в ядре). При каждом обращении к <code>read</code> читается следующая часть файла, например очередная введенная строка. Подпрограмма <code>read</code> также сообщает число прочитанных байтов файла, поэтому конец файла обнаруживается, когда она сообщает: "прочитано 0 байт". Если какие-либо байты оставались в файле, то подпрограмма <code>read</code> выдала хотя бы часть их. На самом деле, отказ от ввода байта со специальным значением "конец файла" вполне оправдан, поскольку, как отмечалось ранее, смысл содержимого байта зависит от интерпретации файла. Но все файлы имеют конец, и поэтому их следует читать с помощью подпрограммы <code>read</code>, а возврат нуля это зависящий от интерпретации способ представления конца файла без использования специальных символов.</p>
    <p>Когда программа читает с вашего терминала, каждая введенная строка передается программе ядром только после ввода символа перевода строки (т.е. нажатия <emphasis>RETURN</emphasis>). Поэтому если вы сделаете ошибки и заметите это до ввода <emphasis>RETURN</emphasis>, можно вернуться и исправить их. Если символ перевода строки введен до того, как вы заметили ошибку, то строка уже прочитана системой и исправить ее нельзя.</p>
    <p>Можно посмотреть ввод по строкам на примере команды <code>cat</code>. Эта команда обычно накапливает или буферизует свой выходной поток, чтобы для повышения эффективности писать большими порциями, но флаг <code>-u</code> отключает буферизацию, так что она выдает строку сразу по получении:</p>
    <p><code>$ cat    </code>Выдача команды cat с буферизацией</p>
    <p><code>123</code></p>
    <p><code>456</code></p>
    <p><code>789</code></p>
    <p><code>ctl-d</code></p>
    <p><code>123</code></p>
    <p><code>456</code></p>
    <p><code>789</code></p>
    <p><code>$ cat -u </code>Выдача команды cat без буферизации</p>
    <p><code>123</code></p>
    <p><code>123</code></p>
    <p><code>456</code></p>
    <p><code>456</code></p>
    <p><code>789</code></p>
    <p><code>789</code></p>
    <p><code>ctl-d</code></p>
    <p><code>$</code></p>
    <p>Команда <code>cat</code> получает каждую строку, когда вы нажимаете клавишу <emphasis>RETURN</emphasis>; без буферизации она выдает данные, как только их получит.</p>
    <p>Теперь попробуем сделать нечто другое: введите несколько символов, а затем вместо <emphasis>RETURN</emphasis> наберите на клавиатуре <emphasis>ctl-d</emphasis>:</p>
    <p><code>$ cat -u 123ctl-d123</code></p>
    <p>Команда <code>cat</code> выдает символы мгновенно. Символ <emphasis>ctl-d</emphasis> означает, что нужно немедленно послать символы, введенные с терминала, программе, которая производит ввод с терминала. В отличие от символа перевода строки <emphasis>ctl-d</emphasis> не передается программе. Теперь введите второй раз <emphasis>ctl-d</emphasis> без каких-либо символов:</p>
    <p><code>$ cat -u</code></p>
    <p><code>123ctl-d123ctl-d$</code></p>
    <p>Интерпретатор отвечает на это выводом приглашения, поскольку команда <code>cat</code>, не получив символов, считает, что файл кончился, и прекращает работу. Символ <emphasis>ctl-d</emphasis> передает все, что вы ввели, программе, производящей ввод с терминала. Если вы ничего не ввели, программа не получит никаких символов, что соответствует концу файла. Именно поэтому ввод <emphasis>ctl-d</emphasis> приводит к выходу из системы интерпретатор не получает больше входной информации. Конечно, символ <emphasis>ctl-d</emphasis> в основном используется как сигнал о конце файла, но он имеет и более общее назначение.</p>
    <cite>
     <subtitle>Упражнение 2.1</subtitle>
     <p>Что произойдет, если ввести <emphasis>ctl-d</emphasis> редактору <code>ed</code>? Сравните этот случай с вводом команды</p>
     <p><code>$ ed &lt; файл</code></p>
    </cite>
   </section>
   <section>
    <title>
     <p>2.2 Что хранится в файле?</p>
    </title>
    <p>Формат файла зависит от программ, которые используют его. Типы файла весьма разнообразны, возможно, потому, что существует большое разнообразие программ. Но, поскольку типы файла не определяются файловой системой, ядро не может указать вам тип файла оно не знает его. Команда <code>file</code> делает обоснованную "догадку" (мы вскоре объясним, как это происходит):</p>
    <p><code>$ file /bin /bin/ed /usr/src/cmd/ed.c /usr/man/man1/ed.1</code></p>
    <p><code>/bin: directory</code></p>
    <p><code>/bin/ed: pure executable</code></p>
    <p><code>/usr/src/cmd/ed.с: c program text</code></p>
    <p><code>/usr/man/man1/ed.1: roff, nroff, or eqn input text</code></p>
    <p>Здесь показаны четыре типичных файла. Все они связаны с редактором: каталог (<code>/bin</code>), в котором находится редактор, двоичный файл или сама программа, готовая к выполнению (<code>/bin/ed</code>), входной текст, т.е. операторы языка Си, составляющие программу (<code>/usr/src/cmd/ed.с</code>), и страница справочного руководства (<code>/usr/man/man1/ed.1</code>).</p>
    <p>При определении типа файла команда <code>file</code> не обращает внимания на имена (хотя могла бы), поскольку соглашения об именах — это всего лишь соглашения и поэтому на них нельзя полагаться полностью. Например, файлы, оканчивающиеся на <code>.с</code>, почти всегда содержат текст программы на языке Си, но ничто не мешает вам создать файл, оканчивающийся на <code>.с</code>, с произвольным содержанием. Команда <code>file</code> читает первые несколько сотен байтов файла и пытается по ним определить тип последнего. (Как мы покажем позднее, файлы специального системного назначения, такие, как каталоги, могут быть идентифицированы путем запроса системы, но эта команда может определить каталог, читая его.)</p>
    <p>Иногда установить тип файла нетрудно. Выполняемая программа помечается вначале двоичным "магическим" числом. Команда <code>od</code>, запущенная без всяких флагов, выдает содержимое файла по словам в 16-разрядном или двухбайтовом представлении, и магическое число становится видимым:</p>
    <p><code>$ od /bin/ed</code></p>
    <p><code>0000000 000410 025000 000462 011444 0000000 000000 000000 000001</code></p>
    <p><code>0000020 170011 016600 000002 005060 1777776 010600 162706 000004</code></p>
    <p><code>0000040 016616 000004 005720 010066 0000002 005720 001376 020076</code></p>
    <p><code>...</code></p>
    <p><code>$</code></p>
    <p>Восьмеричное число 410 отмечает готовую к выполнению программу, которую могут разделять несколько процессов. (Конкретные магические числа зависят от системы.) Набор разрядов, представляющий 410, не является символом из множества ASCII, поэтому он не может быть создан непреднамеренно такими программами, как редактор. Но, конечно, вы можете создать подобный файл, запустив свою собственную программу, а система в соответствии с соглашением сочтет его выполняемой программой.</p>
    <p>В случае текстовых файлов указание может быть скрыто более глубоко в файле, поэтому команда <code>file</code> отыскивает строки, подобные <code>#include</code>, чтобы распознать текст программы на Си, или строки, начинающиеся с точки, чтобы распознать входные данные для программ <code>nroff</code> или <code>troff</code>.</p>
    <p>У вас может возникнуть вопрос: почему система не следит за типами файлов более внимательно, ведь тогда, например, программе <code>sort</code> в качестве входного потока никогда не попадал бы файл <code>/bin/ed</code>. Одна из причин состоит в том, чтобы не потерять какие- нибудь полезные для программиста свойства. Хотя команда</p>
    <p><code>$ sort /bin/ed</code></p>
    <p>не имеет особого смысла, существуют команды, которые могут выполняться с любыми файлами, и нет причин ограничивать их возможности. Команды <code>od</code>, <code>cp</code>, <code>wc</code>, <code>cmp</code>, <code>file</code> и многие другие обрабатывают файлы независимо от их содержания. Но идея бестиповых файлов этим не ограничивается. Если, скажем, для программы <code>nroff</code> входные данные отличаются от текста программы на Си, редактор будет вынужден различать их, создавая файл, и, вероятно, считывая файл для редактирования. Тогда, без сомнения, авторам этой книги было бы трудно подготавливать примеры на языке Си для глав 6, 7 и 8.</p>
    <p>Система UNIX пытается сгладить все различия. Любой текст состоит из строк, заканчивающихся символом перевода строки, и большинство программ воспринимает такой простой формат. В процессе работы над книгой мы много раз запускали команды для создания текстовых файлов, затем обрабатывали эти файлы с использованием команд, подобных уже рассмотренным, и применяли редактор, чтобы слить файлы во входной поток для форматирующей программы <code>troff</code>, с помощью которой подготовлен текст книги. Практически любая страница здесь получена подобным образом:</p>
    <p><code>$ od -с junk &gt; temp</code></p>
    <p><code>$ ed ch2.1</code></p>
    <p><code>1534</code></p>
    <p><code>r temp</code></p>
    <p><code>168</code></p>
    <p><code>...</code></p>
    <p>Команда <code>od</code> передает текст в стандартный выходной поток, который можно использовать там же, где и сам текст. Такая универсальность непривычна; в большинстве систем имеется несколько форматов файла, даже для текста, и требуется диалог с системой со стороны программы или пользователя, чтобы создать файл определенного типа. В системе UNIX есть только один вид файла, и для доступа к такому файлу достаточно лишь знать его имя.<a l:href="#n6" type="note">[6]</a></p>
    <p>Отсутствие форматов файла дает выигрыш во всем программисты не должны заботиться о типе файла, а все стандартные программы будут работать с любым файлом. Однако необходимо отметить и недостатки. Программы, которые сортируют, редактируют, проводят поиск, действительно ожидают текст в качестве входного потока: команда <code>grep</code> не может правильно анализировать двоичные файлы, команда <code>sort</code> не может сортировать их, и никакой стандартный редактор не может работать с ними.</p>
    <p>В большинстве программ, которые ожидают текст в качестве входного потока, существуют ограничения реализации. Мы проверили несколько программ на тексте размером 30 тыс. байт, не содержащем ни одного символа перевода строки, и только некоторые из них работали правильно, поскольку многие программы делают явно неоговариваемые допущения о максимальном размере строки текста (исключительные ситуации см. в разделе BUGS (ошибки) описания <code>sort(1)</code>).</p>
    <p>Нетекстовые файлы, несомненно, имеют свою специфику. Например, для очень больших баз данных обычно нужна дополнительная адресная информация, обеспечивающая быстрый доступ; для повышения эффективности она должна быть представлена в двоичном виде. Но каждому формату файла, не являющемуся текстовым, должно соответствовать свое семейство программ, выполняющее те операции, которые для текстовых файлов производят стандартные средства. Машинная обработка текстовых файлов, возможно, несколько менее эффективна, но это уравновешивается стоимостью дополнительного программного обеспечения, поддерживающего более сложные форматы. Если вы выбираете формат файла, следует тщательно все продумать, прежде чем остановиться на нетекстовом представлении. (Желательно также предусмотреть вариант, при котором поведение вашей программы будет "осмысленным" в случае длинных входных строк.)</p>
   </section>
   <section>
    <title>
     <p>2.3. Каталоги и имена файлов</p>
    </title>
    <p>Все ваши файлы имеют вполне определенные имена, начиная с <code>/usr/you</code>, но если у вас есть только файл <code>junk</code>, то при задании команды <code>ls</code> не выдается <code>/usr/you/junk</code>; имя файла выводится без всякого префикса:</p>
    <p><code>$ ls</code></p>
    <p><code>junk</code></p>
    <p><code>$</code></p>
    <p>Это происходит потому, что любая выполняемая программа, т.е. каждый процесс, имеет <emphasis>текущий каталог</emphasis> и неявно предполагается, что все имена файлов начинаются с имени этого каталога, если они явно не начинаются с дробной черты. Таким образом, у интерпретатора <code>shell</code>, в который вы вошли, и команды <code>ls</code> есть текущий каталог. Команда <code>pwd</code> ("print working directory" печать текущего каталога) выдает имя текущего каталога:</p>
    <p><code>$ pwd</code></p>
    <p><code>/usr/you</code></p>
    <p><code>$</code></p>
    <p>Текущий каталог представляет собой атрибут процесса, а не пользователя или программы: пользователям соответствуют начальные каталоги, а процессам — текущие. Если процесс порождает процесс-потомка, последний наследует текущий каталог родителя. Но, если затем потомок сменит текущий каталог, родителя это не затронет: его текущий каталог останется тем же, независимо от действий потомка.</p>
    <p>Понятие текущего каталога, конечно, обеспечивает удобство обозначения, поскольку освобождает от излишнего ввода, но настоящее его назначение — организационное. Связанные друг с другом файлы находятся в одном каталоге. Каталог <code>/usr</code> обычно является начальным каталогом файловой системы пользователей (<code>usr</code> — сокращение от user, подобно <code>cmp</code>, <code>ls</code> и т.д.). Ваш начальный каталог <code>/usr/you</code> — это ваш текущий каталог при первом вхождении в систему. Каталог <code>/usr/src</code> содержит исходные тексты системных программ, каталог <code>/usr/src/cmd</code> — исходные тексты команд UNIX, <code>/usr/src/cmd/sh</code> — исходные тексты интерпретатора <code>shell</code> и т.д. Всякий раз, когда вы приступаете к новому проекту или когда у вас появляется группа связанных файлов, скажем, набор рецептов, вы можете создать новый каталог с помощью команды <code>mkdir</code> и поместить в него файлы.</p>
    <p><code>$ pwd</code></p>
    <p><code>/usr/you</code></p>
    <p><code>$ mkdir recipes</code></p>
    <p><code>$ cd recipes</code></p>
    <p><code>$ pwd</code></p>
    <p><code>/usr/you/recipes</code></p>
    <p><code>$ mkdir pie cookie</code></p>
    <p><code>$ ed pie/apple</code></p>
    <p><code>...</code></p>
    <p><code>$ ed</code></p>
    <p><code>...</code></p>
    <p><code>$</code></p>
    <p>Заметьте, как легко ссылаться на вложенные каталоги. Файл <code>pie/apple</code> имеет очевидный смысл: рецепт яблочного пирога из каталога <code>/usr/you/recipes/pie</code>. Вместо этого вы могли бы поместить рецепт, например, в каталог <code>recipes/apple.pie</code>, а не во вложенный каталог в каталоге <code>recipes</code>, но лучшее решение — собрать все рецепты пирогов вместе. Так, рецепт крема мог бы храниться в <code>recipes/pie/crust</code>, чтобы не дублировать его в рецепте для каждого пирога. Хотя файловая система предоставляет мощное средство организации данных, вы можете забыть, куда помещен файл, и даже какие файлы у вас есть. Естественным решением было бы иметь одну или несколько команд, позволяющих "порыться" в каталогах. Конечно, команда <code>ls</code> помогает искать файлы, но не дает возможности исследовать вложенные каталоги:</p>
    <p><code>$ cd</code></p>
    <p><code>$ ls</code></p>
    <p><code>junk</code></p>
    <p><code>recipes</code></p>
    <p><code>$ file *</code></p>
    <p><code>junk: ascii text</code></p>
    <p><code>recipes: directory</code></p>
    <p><code>$ ls recipes</code></p>
    <p><code>cookie</code></p>
    <p><code>pie</code></p>
    <p><code>$ ls recipes/pie</code></p>
    <p><code>apple</code></p>
    <p><code>crust</code></p>
    <p><code>$</code></p>
    <p>Эту часть файловой системы можно изобразить графически:</p>
    <image l:href="#img_3.jpeg"/>
    <p><emphasis>Рис. 2.1</emphasis>: Часть файловой системы</p>
    <empty-line/>
    <p>С помощью команды <code>du</code> ("disk usage" — использование диска) вы можете выяснить, какое пространство на диске занято файлами каталога, включая все вложенные каталоги:</p>
    <p><code>$ du</code></p>
    <p><code> 6 ./recipes/pie</code></p>
    <p><code> 4 ./recipes/cookie</code></p>
    <p><code>11 ./recipes</code></p>
    <p><code>13 .</code></p>
    <p><code>$</code></p>
    <p>Смысл имен файлов понятен; числа соответствуют количеству блоков на диске (обычно размер блока составляет 512 или 1024 байта) для хранения каждого файла. При использовании каталога число показывает, сколько блоков задействовано всеми файлами этого каталога, включая вложенные каталоги и сам каталог.</p>
    <p>Команда <code>du</code> имеет флаг <code>-a</code> ("all" — все), который означает, что требуется выдавать все файлы в каталоге. Если один из них является каталогом, команда <code>du</code> сообщает и о нем:</p>
    <p><code>$ du -а</code></p>
    <p><code> 2 ./recipes/pie/apple</code></p>
    <p><code> 3 ./recipes/pie/crust</code></p>
    <p><code> 6 ./recipes/pie</code></p>
    <p><code> 3 ./recipes/cookie/choc.chip</code></p>
    <p><code> 4 ./recipes/cookie</code></p>
    <p><code>11 ./recipes</code></p>
    <p><code> 1 ./junk</code></p>
    <p><code>13 .</code></p>
    <p><code>$</code></p>
    <p>Выходной поток команды <code>du -a</code> можно направить по программному каналу через команду grep для поиска каких-либо файлов:</p>
    <p><code>$ du -a | grep choc</code></p>
    <p><code> 3 ./recipes/cookie/choc.chip</code></p>
    <p><code>$</code></p>
    <p>Напомним (см. гл. 1), что имя <code>'.'</code> — это запись в каталоге, обозначающая сам каталог; оно обеспечивает доступ к каталогу в тех случаях, когда не известно его полное имя. Команда <code>du</code> просматривает файлы в каталоге, причем если вы не указали, в каком именно каталоге следует производить поиск, то она выберет <code>'.'</code>, т. е. каталог, с которым вы работаете в данный момент. Значит, <code>junk</code> и <code>./junk</code> — имена одного и того же файла.</p>
    <p>Несмотря на то, что каталоги играют в системе важную роль, они представляются в файловой системе как обычные файлы. Эти каталоги можно читать, но в отличие от традиционных файлов их нельзя создавать и в них нельзя писать. Для сохранения целостности системы и файлов пользователей ядро берет на себя контроль за содержимым каталогов.</p>
    <p>Теперь представим содержимое каталога в байтовой форме:</p>
    <p><code>$ od -cb</code></p>
    <p><code>000000  4   ;   .  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0</code></p>
    <p><code>       064 073 056 000 000 000 000 000 000 000 000 000 000 000 000</code></p>
    <p><code>000020 273  (   .   .  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0</code></p>
    <p><code>       273 050 056 056 000 000 000 000 000 000 000 000 000 000 000</code></p>
    <p><code>000040 252  ;   p   е   ц   е   п   т   ы  \0  \0  \0  \0  \0  \0</code></p>
    <p><code>       252 073 256 243 263 243 255 260 273 000 000 000 000 000 000</code></p>
    <p><code>000060 230  =   j   u   n   k  \0  \0  \0  \0  \0  \0  \0  \0  \0</code></p>
    <p><code>       230 075 152 165 156 153 000 000 000 000 000 000 000 000 000</code></p>
    <p><code>000100 $</code></p>
    <p>Видите имена файлов, "спрятанные" здесь? Формат каталога — это комбинация двоичного и текстового представлений. Каталог строится из фрагментов по 16 байт, причем последние 14 байт здесь содержат имя файла, дополненное символом NUL из ASCII (нулевой код, имеющий значение 0), а первые два байта указывают системе, где находится служебная информация, относящаяся к файлу (мы вернемся к этому вопросу позднее). Каждый каталог начинается двумя записями: <code>'.'</code> (точка) и <code>'..'</code> (точка-точка).</p>
    <p><code>$ cd         </code>Начальный каталог</p>
    <p><code>$ cd recipes</code></p>
    <p><code>$ pwd</code></p>
    <p><code>/usr/you/recipes</code></p>
    <p><code>$ cd ..; pwd </code>На один уровень выше</p>
    <p><code>/usr/you</code></p>
    <p><code>$ cd ..; pwd </code>Еще на один уровень выше</p>
    <p><code>/usr</code></p>
    <p><code>$ cd ..; pwd </code>Еще на один уровень выше</p>
    <p><code>/</code></p>
    <p><code>$ cd ..; pwd </code>Еще на один уровень выше</p>
    <p><code>/            </code>Выше некуда</p>
    <p><code>$</code></p>
    <p>Каталог <code>/</code> называется <emphasis>корнем</emphasis> файловой системы. Каждый файл системы находится в корневом каталоге или в одном из вложенных в него каталогов, и корневой каталог является родителем самому себе.</p>
    <cite>
     <subtitle>Упражнение 2.2</subtitle>
     <p>На основании изложенного выше представьте приблизительно действие команды <code>ls</code>. <emphasis>Подсказка</emphasis>: <code>cat . &gt; foo; ls -f foo</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 2.3</subtitle>
     <p>(<emphasis>Более сложное</emphasis>) Как действует команда <code>pwd</code>?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 2.4</subtitle>
     <p>Команда <code>du</code> предназначена для учета использования дискового пространства. Осуществлять с ее помощью поиск файлов в иерархии каталогов — довольно странное решение, возможно, даже неподходящее. За альтернативой обратитесь к странице справочного руководства <code>find(1)</code> и сравните две команды, в частности <code>du -a | grep ...</code> и <code>find</code>. Какая из них сработает быстрее? Что лучше: создать новую команду или воспользоваться побочным эффектом уже существующей?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>2.4. Права доступа</p>
    </title>
    <p>В отношении каждого файла существуют права доступа, которые определяют, кто и что может делать с файлом. Если вы, допустим, храните свои любовные письма в системе и даже собрали их в отдельный каталог в иерархии каталогов, то, вероятно, вряд ли захотите, чтобы кто-либо мог их прочитать. Поэтому вы можете изменить права доступа к каждому письму во избежание появления слухов (или только к части писем, возбудив тем самым чье-то любопытство). Вы можете, наконец, просто изменить права доступа к каталогу, содержащему письма, и пресечь любые попытки вмешаться в ваши дела.</p>
    <p>Однако мы должны предупредить вас: в каждой системе UNIX есть особый пользователь, называемый <emphasis>суперпользователем</emphasis>, который может читать или изменять <emphasis>любой</emphasis> файл в системе. Привилегии входа в систему суперпользователю обеспечивает специальное имя <code>root</code> (корень). Это имя используется администраторами системы для выполнения работ по ее поддержанию. Существует команда <code>su</code>, которая гарантирует вам статус суперпользователя при условии, что вы знаете пароль при входе под именем <code>root</code>. Таким образом, всякий, кто знает пароль суперпользователя, может читать ваши любовные письма, поэтому не стоит хранить в системе частную информацию.</p>
    <p>Если нужна большая защищенность, то можно изменить данные в файле так, что даже суперпользователь не сможет прочесть (или, по крайней мере, понять) их, для чего служит команда <code>crypt</code> (см. <code>crypt(1)</code>). Конечно, даже эта команда не дает абсолютной защиты. Суперпользователь может изменить команду <code>crypt</code>, или кто-то попытается расшифровать алгоритм <code>crypt</code>. Правда, и то и другое является трудно выполнимым, так что практически команда <code>crypt</code> достаточно надежна.</p>
    <p>Большинство нарушений защиты происходит из-за паролей, которые выдаются или легко отгадываются. Изредка по недосмотру администратора системы пользователь-злоумышленник получает права суперпользователя. Вопросы безопасности более подробно обсуждаются в статьях, которые указаны в списке литературы в конце главы.</p>
    <p>При входе в систему вы вводите имя и подтверждаете, что это вы, задавая пароль. Имя представляет собой ваш входной идентификатор, или login-id. На самом деле, система распознает вас по числу, называемому идентификатором пользователя, или uid. В действительности различным login-id может соответствовать один uid, что делает их неразличимыми для системы, хотя такое бывает относительно редко, и, по всей видимости, является нежелательным по соображениям безопасности. Кроме uid, вам приписывается идентификатор группы, или group-id, который относит вас к определенной группе пользователей. Во многих системах обычных пользователей (в отличие от тех, кто имеет login-id типа <code>root</code>) объединяют в одну группу под именем <code>other</code> (другие), но в вашей системе может быть иначе. Файловая система, а значит, и вся система UNIX в целом определяет ваши возможности исходя из прав доступа, предоставляемых вашему uid и group-id.</p>
    <p>Файл <code>/etc/passwd</code> — это файл <emphasis>паролей</emphasis>; он содержит всю информацию, связанную со входом каждого пользователя в систему. Подобно системе, вы можете определить свой uid и group-id, если найдете свое имя в <code>/etc/passwd</code>:</p>
    <p><code>$ grep you /etc/passwd</code></p>
    <p><code>you:gkmbCTrJ04C0M:604:1:Y.0.А.People:/usr/you:</code></p>
    <p><code>$</code></p>
    <p>Поля в файле паролей разделяются двоеточием и расположены следующим образом (как видим из <code>passwd(5)</code>):</p>
    <p><code>login-id:зашифрованный_пароль:uid:group-id:разное:начальный_каталог:shell</code></p>
    <p>Файл паролей представляет собой обычный текстовый файл, но назначение и разделитель полей определяются по соглашению между программами, работающими с информацией этого файла. Поле shell обычно пустое; значит, по умолчанию используется стандартный интерпретатор <code>/bin/sh</code>. Поле "разное" может содержать что угодно (как правило, ваше имя, адрес или телефон).</p>
    <p>Заметьте, что ваш пароль присутствует здесь во втором поле, но в зашифрованном виде. Файл паролей могут прочесть все (вы только что это сделали), и если ваш пароль бы там, то любой, кто пожелает, может выдать себя за вас. Когда вы вводите свой пароль при входе в систему, он шифруется, и результат сравнивается с зашифрованным паролем из <code>/etc/passwd</code>. Если они совпадают, то вам разрешают войти. Этот механизм работоспособен, потому что алгоритм шифрации таков, что позволяет легко перейти от раскрытой формы к зашифрованной, тогда как обратный переход очень труден. Например, если ваш пароль <code>ka-boom</code>, он может быть зашифрован как <code>gkmbCTrJ04COM</code>, но, получив последний, вам будет нелегко вернуться к оригиналу.</p>
    <p>Ядро решает, что вам можно позволить читать файл <code>/etc/passwd</code> исходя из прав доступа, связанных с файлом. Для каждого файла предусмотрены три вида прав доступа: чтение (т.е. исследование его содержимого), запись (т. е. изменение его содержимого) и выполнение (т. е. запуск его как программы). Далее, разным пользователям могут быть предоставлены различные права доступа. Как владелец файла вы имеете один набор прав на чтение, запись и выполнение. У "вашей" группы — другой набор прав доступа, у всех остальных — третий набор.</p>
    <p>Команда <code>ls</code> с флагом <code>-l</code> сообщает среди прочего права доступа:</p>
    <p><code>$ ls -l /etc/passwd</code></p>
    <p><code>-rw-r--r-- 1 root 5115 Aug 30 10:40 /etc/passwd</code> </p>
    <p><code>$ ls -lq /etc/passwd</code></p>
    <p><code>-rw--r--r-- 1 adm 5115 Aug 30 10:40 /etc/passwd</code></p>
    <p>Информацию, содержащуюся в двух строках вывода команды <code>ls</code>, можно интерпретировать так: владельцем файла <code>/etc/passwd</code> является пользователь с login-id, равным <code>root</code>; его группа называется <code>adm</code>; размер файла 5115 байт; последний раз изменен был 30 августа в 10:40; файл имеет единственную связь, т.е. одно имя в файловой системе (вопрос о связях мы обсудим в следующем разделе). Некоторые варианты команды <code>ls</code> выдают имена владельца и группы сразу при однократном вызове.</p>
    <p>Строка <code>-rw-r--r--</code> показывает, как представляет права доступа к файлу команда <code>ls</code>. Первый дефис (<code>-</code>) означает, что это обычный файл. В случае каталога на его месте стояла бы буква <code>d</code>. Следующие три символа обозначают права владельца файла на чтение, запись и выполнение (исходя из uid). Строка <code>rw-</code> свидетельствует о том, что владелец (<code>root</code>) может читать, писать, но не выполнять файл. В случае выполняемого файла дефис был бы заменен символом <code>x</code>.</p>
    <p>Три символа (<code>r--</code>) обозначают права доступа группы, в данном случае пользователей из группы <code>adm</code> — по-видимому, системных администраторов, которые могут читать файл, но не писать и не выполнять его. Следующие три символа (также <code>r--</code>) определяют права доступа для всех остальных пользователей системы. Таким образом, на данной машине только <code>root</code> может изменить информацию по входу в систему для пользователя, но прочесть файл и узнать эту информацию может любой. Разумным был бы вариант, при котором группа <code>adm</code> также имела бы право на запись в файл <code>/etc/passwd</code>.</p>
    <p>Файл <code>/etc/group</code> хранит в зашифрованном виде имена групп и их group-id и определяет, какие пользователи входят в какие группы. В файле <code>/etc/passwd</code> определяется только ваша группа при входе в систему; команда <code>newgrp</code> изменяет ее права доступа на права другой группы.</p>
    <p>Кто угодно может задать:</p>
    <p><code>$ ed /etc/passwd</code></p>
    <p>и редактировать файл паролей, но только <code>root</code> может записать измененный файл. Поэтому вполне правомочен вопрос: как изменить свой пароль, если это требует редактирования файла паролей. Программа, изменяющая пароли, называется <code>passwd</code>, вероятно, вы найдете ее в <code>/bin</code>:</p>
    <p><code>$ ls -l /bin/passwd</code></p>
    <p><code>-rwsr-xr-x 1 root 8454 Jan 4 1983 /bin/passwd</code></p>
    <p><code>$</code></p>
    <p>(Обратите внимание на то, что <code>/etc/passwd</code> — текстовый файл, содержащий информацию по входу в систему, тогда как <code>/bin/passwd</code> находится в другом каталоге, содержит программу, готовую к выполнению, и позволяет изменить данные, связанные с паролем). Права доступа к этому файлу показывают, что выполнить команду может кто угодно, но изменить команду <code>passwd</code> — только <code>root</code>. Буква <code>s</code> вместо <code>x</code> в поле прав на выполнение для владельца файла означает, что при выполнении команды ей предоставляются права, соответствующие праву владельца файла, в данном случае <code>root</code>. Поскольку файл <code>/bin/passwd</code> имеет такой признак установки uid и при выполнении получает права <code>root</code>, любой пользователь, выполняя команду <code>passwd</code>, может редактировать файл <code>/etc/passwd</code>.</p>
    <p>Введение признака установки uid — простое элегантное решение целого ряда проблем безопасности.<a l:href="#n7" type="note">[7]</a> Например, автор игровой программы может установить свой uid для программы, поэтому она сможет изменять файл с результатами игр, который защищен от доступа со стороны других пользователей. Но идея введения признака установки uid потенциально опасна. Программа <code>/bin/passwd</code> должна быть правильной, иначе она может уничтожить системную информацию под прикрытием суперпользователя root. При наличии прав доступа <code>-rwsrwxrwx</code> ее мог бы переписать любой пользователь, и, таким образом, заменить файл на неработоспособную программу. Это особенно опасно для программ, обладающих признаком установки uid, поскольку <code>root</code> имеет доступ к каждому файлу, системы. (В некоторых системах UNIX происходит отключение признака установки uid всякий раз, когда файл изменяется, что уменьшает вероятность нарушения защиты).</p>
    <p>Признак установки uid — мощное средство, но оно используется в основном для нескольких системных программ, таких, как <code>passwd</code>. Рассмотрим более типичный файл:</p>
    <p><code>$ ls -l /bin/who</code></p>
    <p><code>-rwxrwxr-x 1 root 6348 Mar 29 1983 /bin/who</code></p>
    <p><code>$</code></p>
    <p>Этот файл доступен для выполнения всем, а писать в него могут только <code>root</code> и пользователь той же группы. Слова "доступен для выполнения" означают, что при вводе</p>
    <p><code>$ who</code></p>
    <p>интерпретатор <code>shell</code> просматривает ряд каталогов, в том числе <code>/bin</code>, отыскивая файл с именем <code>who</code>. Если такой файл найден и он имеет право доступа на выполнение, то <code>shell</code> обращается к ядру для его запуска. Ядро проверяет права доступа, и, если они действительны, запускает программу. Отметим, что программа — это просто файл с правом доступа на выполнение. В следующей главе вы познакомитесь с программами, являющимися обычными текстовыми файлами, но они могут выполняться как команды, поскольку имеют право доступа на выполнение.</p>
    <p>Права доступа к каталогам действуют несколько иначе, но основной принцип остается тем же:</p>
    <p><code>$ ls -ld .</code></p>
    <p><code>drwxrwxr-x 3 you 80 Sep 27 06:11 .</code></p>
    <p><code>$</code></p>
    <p>Команда <code>ls</code> с флагом <code>-d</code> сообщает скорее о самом каталоге, чем о его содержимом, и первая буква <code>d</code> в выводе означает, что <code>'.'</code> в действительности является каталогом. Поле <code>r</code> показывает, что можно читать каталог, поэтому с помощью команды <code>ls</code> (или <code>od</code> для данного случая) можно выяснить, какие файлы хранятся в нем. Буква <code>w</code> свидетельствует о том, что можно создавать и исключать файлы из каталога, поскольку это требует изменения, а значит, записи в файл каталога.</p>
    <p>На самом деле нельзя просто писать в каталог, даже суперпользователю <code>root</code> это запрещено:</p>
    <p><code>$ who &gt; .        </code>Попытка затереть '.'</p>
    <p><code>.: cannot create </code>Нельзя</p>
    <p><code>$</code></p>
    <p>Существуют системные обращения, которые создают и удаляют файлы, и только с их помощью можно изменить содержимое каталога. Но принцип прав доступа применим и для них: поле <code>w</code> показывает, кто может использовать системные функции для изменения каталога.</p>
    <p>Право на удаление файла не зависит от самого файла. Если у вас есть право записи в каталог, вы можете удалять файлы из него, причем даже те, которые защищены от записи. Команда <code>rm</code> все-таки запрашивает подтверждение, прежде чем удалить защищенный файл, чтобы убедиться, что вы действительно хотите это сделать, — редкий для команд системы UNIX случай двойной проверки намерений пользователя. (Флаг <code>-f</code> команды rm обеспечивает удаление файлов без запроса.)</p>
    <p>Поле <code>x</code> в случае каталога означает не выполнение, а поиск. Право на выполнение определяет возможность поиска файла в каталоге. Поэтому возможно создать каталог с правом доступа "<code>x</code>" для других пользователей, предполагая, что пользователи будут иметь доступ к любому известному им файлу в каталоге, но не смогут выполнять команду <code>ls</code> или читать каталог, чтобы узнать, какие файлы в нем находятся. Аналогично каталог с правом доступа <code>r--</code> можно читать (с помощью <code>ls</code>), но нельзя работать с его файлами. В некоторых системах используют это свойство, чтобы закрыть каталог <code>/usr/games</code> в рабочее время.</p>
    <p>Команда <code>chmod</code> ("change mode" — изменить режим) меняет права доступа к файлам:</p>
    <p><code>$ chmod права_доступа имена файлов...</code></p>
    <p>Синтаксис конструкции "права_доступа", к сожалению, громоздкий. Она может определяться двумя способами: с помощью восьмеричных чисел и последовательностью символов. Проще использовать восьмеричные числа, хотя иногда более удобными оказываются символьные обозначения, так как с их помощью можно показать, какие изменения произошли в правах доступа. Лучше, конечно, было бы задать</p>
    <p><code>$ chmod rw-rw-rw- junk</code> Так нельзя!</p>
    <p>чем вводить</p>
    <p><code>$ chmod 666 junk</code></p>
    <p>но так не получается. Восьмеричное значение режима складывается из значений прав доступа: 4 — для чтения, 2 — для записи и 1 — для выполнения. Три цифры, как и в выводе команды <code>ls</code>, показывают права доступа для владельца, группы и всех остальных. Символьные обозначения объяснить труднее; их точное описание приводится в справочном руководстве <code>chmod(1)</code>. Для наших же целей достаточно указать, что "<code>+</code>" устанавливает право доступа, а "<code>-</code>" лишает его. Например,</p>
    <p><code>$ chmod +x command</code></p>
    <p>позволяет всем выполнять команду, а</p>
    <p><code>$ chmod -w file</code></p>
    <p>лишает всех права записи в файл, включая и владельца файла. Если не принимать во внимание существование суперпользователя, то только владелец файла может изменить права доступа к файлу, независимо от текущих прав доступа. В том случае, когда кто-то еще предоставил вам право записи в файл, система не позволит изменить код прав доступа к файлу:</p>
    <p><code>$ ls -ld /usr/mary</code></p>
    <p><code>drwxrwxrwx 5 mary 704 Sep 25 10:18 /usr/mary</code></p>
    <p><code>$ chmod 444 /usr/mary</code></p>
    <p><code>chmod: can't change /usr/mary</code></p>
    <p><code>$</code></p>
    <p>Но, если каталог не защищен от записи, пользователи могут удалять файлы, несмотря на отсутствие права доступа к файлу. Если вы хотите быть уверенным в том, что ни вы, ни ваши друзья никогда не удалят файлы из каталога, отмените право на запись для каталога:</p>
    <p><code>$ cd</code></p>
    <p><code>$ date &gt; temp</code></p>
    <p><code>$ chmod -w .         </code>Закрыть каталог по записи</p>
    <p><code>$ ls -ld .</code></p>
    <p><code>dr-xr-xr-x 3 you 80 Sep 27 11:48 .</code></p>
    <p><code>$ rm temp</code></p>
    <p><code>rm: temp not removed </code>Нельзя удалить файл</p>
    <p><code>$ chmod 775 .        </code>Восстановление прав доступа</p>
    <p><code>$ ls -ld .</code></p>
    <p><code>drwxrwxr-x 3 you 80 Sep 27 11:48 .</code></p>
    <p><code>$ rm temp            </code>Теперь можно</p>
    <p><code>$</code></p>
    <p>Файл <code>temp</code> теперь удален. Обратите внимание на то, что изменение прав доступа к каталогу не меняет дату последней модификации файла. Дата модификации отражает изменение содержимого файла, а не прав доступа к нему. Права доступа и даты хранятся не в самом файле, а в системной структуре данных, называемой индексным дескриптором (речь о нем пойдет в следующем разделе).</p>
    <cite>
     <subtitle>Упражнение 2.5</subtitle>
     <p>Поэкспериментируйте с командой <code>chmod</code>. Попробуйте разные простые варианты типа 0 или 1. Будьте осторожны, чтобы не испортить свой начальный каталог.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>2.5 Индексные дескрипторы</p>
    </title>
    <p>Файл имеет несколько атрибутов: имя, содержимое и служебную информацию (права доступа и даты модификации). Служебная информация размещается в индексном дескрипторе вместе с важной системной информацией, такой, как размер файла, место хранения его на диске и т.д.<a l:href="#n8" type="note">[8]</a> В индексном дескрипторе хранятся три даты: время последнего изменения файла (записи в него), время последнего использования файла (чтение или выполнение), время последнего изменения самого индексного дескриптора, например изменения прав доступа.</p>
    <p><code>$ date</code></p>
    <p><code>Tue Sep 27 12:07:24 EDT 1983 $ date &gt; junk</code></p>
    <p><code>$ ls -l junk</code></p>
    <p><code>-rw-rw-rw 1 you 29 Sep 27 12:07 junk</code></p>
    <p><code>$ ls -lu junk</code></p>
    <p><code>-rw-rw-rw 1 you 29 Sep 27 06:11 junk</code></p>
    <p><code>$ ls -lc junk</code></p>
    <p><code>-rw-rw-rw 1 you 29 Sep 27 12:07 junk</code></p>
    <p><code>$</code></p>
    <p>Как видно из результата действия команды <code>ls -lu</code>, изменение содержимого файла не влияет на дату последнего использования, а с изменением прав доступа связана только дата изменения индексного дескриптора, о чем выдается сообщение командой <code>ls -lc</code>:</p>
    <p><code>$ chmod 444 junk</code></p>
    <p><code>$ ls -lu junk</code></p>
    <p><code>-r--r--r-- 1 you 29 Sep 27 06:11 junk</code></p>
    <p><code>$ ls -lc junk</code></p>
    <p><code>-r--r--r-- 1 you 29 Sep 27 12:11 junk</code></p>
    <p><code>$ chmod 666 junk $</code></p>
    <p>Можно использовать флаг <code>-t</code> команды <code>ls</code>, который применяется для сортировки файлов по времени (по умолчанию принимается время последней модификации), совместно с флагами <code>-с</code> или <code>-r</code>, чтобы узнать порядок, в котором изменились индексные дескрипторы или читались файлы:</p>
    <p><code>$ ls recipes</code></p>
    <p><code>apple</code></p>
    <p><code>pie</code></p>
    <p><code>$ ls -lut total 2</code></p>
    <p><code>drwxrwxrwx 4 you 64 Sep 27 12:11 recipes</code></p>
    <p><code>-rw-rw-rw- 1 you 29 Sep 27 06:11 junk</code></p>
    <p>У каталога <code>recipes</code>, как вы видите, более позднее время использования, поскольку мы только что просмотрели его содержимое.</p>
    <p>Очень важное понять значение индексного дескриптора, причем не для того, чтобы оценить действие флагов команды <code>ls</code>. По существу, индексные дескрипторы и есть файлы. Иерархия каталогов предоставляет только удобный способ именования файлов. Внутреннее системное имя файла или индекс файла — это номер индексного дескриптора, содержащего информацию о файле. Команда <code>ls -i</code> выдает индекс файла в десятичной форме:</p>
    <p><code>$ date &gt; x</code></p>
    <p><code>$ ls -i</code></p>
    <p><code>15768 junk</code></p>
    <p><code>15274 recipes</code></p>
    <p><code>15852 x</code></p>
    <p><code>$</code></p>
    <p>Именно индекс файла хранится в первых двух байтах каталога, предшествующих имени. Команда <code>od -d</code> выдает информацию не в восьмеричной форме по байтам, а в десятичной, объединив по два байта в одно целое, и поэтому мы увидим на экране индекс файла:</p>
    <p><code>$od -с .</code></p>
    <p><code>0000000   4  ; . \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0</code></p>
    <p><code>0000020 273 (  .  . \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0</code></p>
    <p><code>0000040 252  ; p  е  ц  п  т  ы \0 \0 \0 \0 \0 \0 \0 \0</code></p>
    <p><code>0000060 230  = j  u  n  к \0 \0 \0 \0 \0 \0 \0 \0 \0 \0</code></p>
    <p><code>0000100 354  = x \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0</code></p>
    <p><code>0000120</code> </p>
    <p><code>od -d .</code></p>
    <p><code>0000000 15156 00046 00000 00000 00000 00000 00000 00000</code></p>
    <p><code>0000020 10427 11822 00000 00000 00000 00000 00000 00000</code></p>
    <p><code>0000040 15274 25970 26979 25968 00115 00000 00000 00000</code></p>
    <p><code>0000060 15768 30058 27502 00000 00000 00000 00000 00000</code></p>
    <p><code>0000100 15852 00120 00000 00000 00000 00000 00000 00000</code></p>
    <p><code>0000120</code></p>
    <p><code>$</code></p>
    <p>Первые два байта в каждой строке каталога являются единственной связью между именем файла и его содержимым. Именно поэтому имя файла в каталоге называется связью: оно связывает имя в иерархии каталогов с индексным дескриптором и, тем самым, с информацией. Один и тот же индекс файла может появиться в нескольких каталогах. Команда <code>rm</code> в действительности удаляет не индексный дескриптор, а строку каталога или связь. Только когда последняя связь файла исчезает, система удаляет индексный дескриптор, а значит, и сам файл.</p>
    <p>Если индекс файла в строке каталога равен нулю, это означает, что связь удалена, но сам файл не обязательно удален — могут существовать связи где-нибудь еще. Можно убедиться в том, что индекс файла становится равным нулю при удалении файла:</p>
    <p><code>$ rm x $ od -d .</code></p>
    <p><code>0000000 15156 00046 00000 00000 00000 00000 00000 00000</code></p>
    <p><code>0000020 10427 11822 00000 00000 00000 00000 00000 00000</code></p>
    <p><code>0000040 15274 25970 26979 25968 00115 00000 00000 00000</code></p>
    <p><code>0000060 15768 30058 27502 00000 00000 00000 00000 00000</code></p>
    <p><code>0000100 00000 00120 00000 00000 00000 00000 00000 00000</code></p>
    <p><code>0000120</code></p>
    <p><code>$</code></p>
    <p>Следующий файл, создаваемый в этом каталоге, займет освободившуюся позицию, хотя у него, вероятно, будет другой индекс. Команда <code>ln</code>, имеющая приведенный ниже синтаксис, устанавливает связь с уже существующим файлом:</p>
    <p><code>$ ln old-file new-file</code></p>
    <p>Назначение связи состоит в том, чтобы дать два имени одному и тому же файлу, поэтому он часто оказывается в двух разных каталогах. Во многих системах есть связь с редактором <code>/bin/ed</code> под названием <code>/bin/е</code>, так что пользователи могут вызывать редактор как <code>е</code>. Две связи одного файла указывают на одну и ту же запись, а значит, имеют один и тот же индекс файла:</p>
    <p><code>$ ln junk linktojunk</code></p>
    <p><code>$ ls -li total 3</code></p>
    <p><code>15768 -rw-rw-rw- 2 you 29 Sep 27 12:07 junk</code></p>
    <p><code>15768 -rw-rw-rw- 2 you 29 Sep 27 12:07 linktojunk</code></p>
    <p><code>15274 drwxrwxrwx 4 you 64 Sep 27 09:34 recipes</code></p>
    <p><code>$</code></p>
    <p>Целое число, выдаваемое между правом доступа и именем владельца файла, является числом связей файла. Поскольку каждая связь ссылается на индексный дескриптор, все связи одинаково, важны — нет разницы между первой связью и последующими. (Заметим, что общий объем занимаемого на диске пространства, сообщаемый командой <code>ls</code>, вычисляется неверно из-за двойного подсчета).</p>
    <p>Если изменить файл, то изменение обнаружится при обращении к файлу под любым из его имен, так как все связи ссылаются на один файл:</p>
    <p><code>$ echo x &gt; junk</code></p>
    <p><code>$ ls -l total 3</code></p>
    <p><code>-rw-rw-rw- 2 you  2 Sep 27 12:37 junk</code></p>
    <p><code>-rw-rw-rw- 2 you  2 Sep 27 12:37 linktojunk</code></p>
    <p><code>drwxrwxrwx 4 you 64 Sep 27 09:34 recipes</code></p>
    <p><code>$ rm linktojunk</code></p>
    <p><code>$ ls -l total 2</code></p>
    <p><code>-rw-rw-rw- 1 you  2 Sep 27 12:37 junk</code></p>
    <p><code>drwxrwxrwx 4 you 64 Sep 27 09:34 recipes</code></p>
    <p><code>$</code></p>
    <p>После удаления файла <code>linktojunk</code> число связей опять становится равным единице. Как уже отмечалось, при удалении файла уничтожается лишь связь. Сам же файл сохраняется до тех пор, пока не будет удалена последняя связь. На практике, конечно, большинство файлов имеет единственную связь, но тем не менее вы убедились в том, что можно простым способом повысить гибкость системы.</p>
    <p>Предостережение тем, кто слишком торопится: после удаления последней связи данные становятся недоступными. Удаленные файлы попадают скорее в топку, чем в мусорную корзину, и нет средства возродить их из пепла. (Слабая надежда на возрождение все-таки есть. В больших системах UNIX есть рутинная функция дублирования, которая периодически копирует изменяемые файлы в какое-нибудь надежное место типа магнитной ленты, откуда их можно извлечь. Для собственного успокоения вам следует знать, какой объем дублирования обеспечивает ваша система. Если нет ничего, будьте бдительны — любые неполадки с дисками могут обернуться катастрофой.)</p>
    <p>Связи файла удобны, если два пользователя совместно работают с ним, но иногда нужна на самом деле <emphasis>отдельная</emphasis> копия — другой файл с той же информацией. Например, вы можете скопировать документ до внесения в него существенных изменений, так что можно будет восстановить оригинал, если вас не устроят эти изменения. Здесь не поможет установление связи, так как при изменении данных обе связи будут ссылаться на измененный файл. Копирует файлы команда <code>cp</code>:</p>
    <p><code>$ cp junk copyofjunk</code></p>
    <p><code>$ ls -li total 3</code></p>
    <p><code>15850 -rw-rw-rw- 1 you  2 Sep 27 13:13 copyofjunk</code></p>
    <p><code>15768 -rw-rw-rw- 1 you  2 Sep 27 12:37 junk</code></p>
    <p><code>15274 drwxrwxrwx 4 you 64 Sep 27 09:34 recipes</code></p>
    <p><code>$</code></p>
    <p>Индексы файлов <code>junk</code> и <code>copyofjunk</code> различны, поскольку это различные файлы, хотя в данный момент они имеют одинаковое содержимое. Существует полезный прием: можно изменить права доступа к копии файла, чтобы ее труднее было случайно удалить.</p>
    <p><code>$ chmod -w copyofjunk     </code>Убрать право записи</p>
    <p><code>$ ls -li total 3</code></p>
    <p><code>15850 -r--r--r-- 1 you  2 Sep 27 13:13 copyofjunk</code></p>
    <p><code>15768 -rw-rw-rw- 1 you  2 Sep 27 12:37 junk</code></p>
    <p><code>15274 drwxrwxrwx 4 you 64 Sep 27 09:34 recipes</code></p>
    <p><code>$ rm copyofjunk</code></p>
    <p><code>rm: copyofjunk 444 mode n </code>Нельзя! Он нужен</p>
    <p><code>$ date &gt; junk</code></p>
    <p><code>$ ls -li total 3</code></p>
    <p><code>15850 -r--r--r-- 1 you  2 Sep 27 13:13 copyofjunk</code></p>
    <p><code>15768 -rw-rw-rw- 1 you 29 Sep 27 13:16 junk</code></p>
    <p><code>15274 drwxrwxrwx 4 you 64 Sep 27 09:34 recipes</code></p>
    <p><code>$ rm copyofjunk</code></p>
    <p><code>rm: copyofjunk 444 mode y </code>А может быть, и не так нужен</p>
    <p><code>$ ls -li total 2</code></p>
    <p><code>15768 -rw-rw-rw- 1 you 29 Sep 27 13:16 junk</code></p>
    <p><code>15274 drwxrwxrwx 4 you 64 Sep 27 09:34 recipes</code></p>
    <p><code>$</code></p>
    <p>Изменение копии файла или ее удаление не оказывает действия на оригинал. Обратите внимание на то, что, поскольку у файла <code>copyofjunk</code> отменено право на запись, команда rm запрашивает подтверждение, прежде чем удалить файл.</p>
    <p>Есть еще одна команда общего назначения, управляющая файлами, — <code>mv</code>, которая переносит или переименовывает файлы, просто преобразуя связи. Синтаксис ее такой же, как у команд <code>cp</code> и <code>ln</code>:</p>
    <p><code>$ mv junk sameoldjunk $ ls -li total 2</code></p>
    <p><code>15274 drwxrwxrwx 4 you 64 Sep 27 09:34 recipes</code></p>
    <p><code>15768 -rw-rw-rw- 1 you 29 Sep 27 13:16 sameoldjunk</code></p>
    <p><code>$</code></p>
    <p><code>sameoldjunk</code> — это тот же самый файл, что и наш старый файл <code>junk</code>, вплоть до индекса файла, который связан с записью каталога с номером 15768; изменилось только его имя.</p>
    <p>Все описанные выше манипуляции с файлами происходили в одном каталоге, однако команды применяются и в других каталогах. Команда <code>ln</code> часто используется для того, чтобы установить связь с одним именем в разных каталогах; это бывает в тех случаях, когда несколько пользователей работают с одной программой или над одним документом. Команда mv может переслать файл или каталог из одного каталога в другой. На самом деле, это довольно стандартный прием, так что у команд <code>mv</code> и <code>cp</code> есть специальный синтаксис для такой ситуации:</p>
    <p><code>$ mv</code> (или <code>cp</code>) <code>file1 file2 ... directory</code></p>
    <p>Здесь показана пересылка (или копирование) одного или нескольких файлов в каталог, который является последним аргументом. Связи или копии файлов получаются под теми же именами. Например, если вы хотите "набить руку" в работе с редактором, то можете начать с</p>
    <p><code>$ cp /usr/src/cmd/ed.с .</code></p>
    <p>получив свою копию исходного текста редактора для экспериментов. Осваивая интерпретатор <code>shell</code>, исходные тексты которого находятся в нескольких каталогах, задайте</p>
    <p><code>$ mkdir sh</code></p>
    <p><code>$ cp /usr/src/cmd/sh/* sh</code></p>
    <p>и команда <code>cp</code> скопирует все исходные тексты <code>shell</code> в ваш вложенный каталог <code>sh</code> (мы считаем, что в <code>/usr/src/cmd/sh</code> нет вложенных каталогов, так как команда <code>cp</code> не слишком "умна"). В некоторых случаях команду <code>ln</code> допустимо применять с несколькими именами файлов в качестве аргументов, но имя каталога по-прежнему является последним аргументом. В ряде систем команды <code>mv</code>, <code>cp</code> и <code>ln</code> сами служат связями, ссылающимися на один файл, который анализирует имя команды, чтобы узнать, какое задание выполнить.</p>
    <cite>
     <subtitle>Упражнение 2.6</subtitle>
     <p>Почему команда <code>ls -l</code> выдает четыре связи у каталога <code>recipes</code>?</p>
     <p><emphasis>Подсказка</emphasis>: попробуйте ввести</p>
     <p><code>$ ls -ld /usr/you</code></p>
     <p>Чем эта информация полезна?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 2.7</subtitle>
     <p>В чем состоит разница между</p>
     <p><code>$ mv junk junk1</code></p>
     <p>и</p>
     <p><code>$ cp junk junk1</code></p>
     <p><code>$ rm junk</code></p>
     <p><emphasis>Подсказка</emphasis>: установите связь с <code>junk</code> и затем используйте ее.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 2.8</subtitle>
     <p>Команда <code>cp</code> не производит копирования во вложенных каталогах, а ограничивается файлами первого уровня вложенности. Каковы будут ее действия, если один из аргументов окажется каталогом? Насколько это хорошо и осмысленно? Обсудите возможные преимущества трех вариантов: включить еще один флаг в <code>cp</code>, чтобы работать с вложенными каталогами, ввести отдельную команду <code>rcp</code> (рекурсивную <code>cp</code>) для данного случая или просто предъявить к <code>cp</code> требование копировать все файлы из каталога, если он встретится среди аргументов (см. гл. 7). Что получат другие программы, если они смогут перемещаться по дереву каталогов?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>2.6 Иерархия каталогов</p>
    </title>
    <p>В первой главе рассмотрение иерархии файловой системы, начиная с каталога <code>/usr/you</code>, носило несколько неформальный характер. Теперь мы хотим изучить ее последовательно, начиная от корня дерева.</p>
    <p>Корневой каталог называется <code>/</code>:</p>
    <p><code>$ ls /</code></p>
    <p><code>bin</code></p>
    <p><code>boot</code></p>
    <p><code>dev</code></p>
    <p><code>etc</code></p>
    <p><code>lib</code></p>
    <p><code>tmp</code></p>
    <p><code>unix</code></p>
    <p><code>usr</code></p>
    <p><code>$</code></p>
    <p>Программа <code>/unix</code> — это программа ядра UNIX: когда система начинает работу, <code>/unix</code> считывается с диска в память и начинает выполняться. Все происходит за два шага: вначале считывается файл <code>/boot</code>, а затем он считывает <code>/unix</code>. Более подробно о таком процессе раскрутки можно узнать в справочном руководстве по <code>boot(8)</code>. Остальные файлы каталога <code>/</code>, по крайней мере в нашей версии, являются каталогами, каждый из которых представляет законченный раздел файловой системы. После дальнейшего краткого обзора иерархии читателю будет предоставлена возможность поэкспериментировать с упоминаемыми здесь каталогами. Чем лучше вы разберетесь в устройстве файловой системы, тем более эффективно сможете ею пользоваться. В табл. 2.1 указаны подходящие места для поиска, хотя некоторые имена каталогов зависят от системы.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>/</code></td>
      <td align="left" valign="top">Корень файловой системы</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/bin</code></td>
      <td align="left" valign="top">Основные программы, готовые к выполнению (двоичные)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/dev</code></td>
      <td align="left" valign="top">Файлы устройств</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/etc</code></td>
      <td align="left" valign="top">"Разное" системы</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/etc/motd</code></td>
      <td align="left" valign="top">Сегодняшнее сообщение при входе в систему</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/etc/passwd</code></td>
      <td align="left" valign="top">Файл паролей</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/lib</code></td>
      <td align="left" valign="top">Основные библиотеки и т.п.</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/tmp</code></td>
      <td align="left" valign="top">Временные файлы; обновляется при запуске системы</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/unix</code></td>
      <td align="left" valign="top">Операционная система в форме, готовой к выполнению</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr</code></td>
      <td align="left" valign="top">Файловая система пользователей</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/adm</code></td>
      <td align="left" valign="top">Системная служба: справочная информация и т.п.</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/bin</code></td>
      <td align="left" valign="top">Команды для пользователей: <code>troff</code> и т.п.</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/games</code></td>
      <td align="left" valign="top">Игровые программы</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/include</code></td>
      <td align="left" valign="top">Файлы определений Си-программ, например <code>math.h</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/include/sys</code></td>
      <td align="left" valign="top">Системные файлы определений Си-программ, например <code>inode.h</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/lib</code></td>
      <td align="left" valign="top">Библиотеки для Си, Фортрана и т.п.</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/man</code></td>
      <td align="left" valign="top">Диалоговое справочное руководство</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/man/man1</code></td>
      <td align="left" valign="top">Страницы справочного руководства раздела 1</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/mdec</code></td>
      <td align="left" valign="top">Диагностика ошибок аппаратуры, программы раскрутки и т.п.</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/news</code></td>
      <td align="left" valign="top">Служба сообщений пользователей</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/pub</code></td>
      <td align="left" valign="top">"Всякая всячина": см. <code>ascii(7)</code> и <code>eqnchar(7)</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/src</code></td>
      <td align="left" valign="top">Исходные тексты служебных функций и библиотек</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/src/cmd</code></td>
      <td align="left" valign="top">Исходные тексты команд из <code>/bin</code> и <code>/usr/bin</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/src/lib</code></td>
      <td align="left" valign="top">Исходные тексты библиотечных функций</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/spool</code></td>
      <td align="left" valign="top">Рабочий каталог для взаимодействующих программ</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/spool/lpd</code></td>
      <td align="left" valign="top">Временный каталог для печатающего устройства</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/spool/mail</code></td>
      <td align="left" valign="top">Почтовые ящики</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/spool/uucp</code></td>
      <td align="left" valign="top">Рабочий каталог программ <code>uucp</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/sys</code></td>
      <td align="left" valign="top">Исходный текст ядра операционной системы</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/tmp</code></td>
      <td align="left" valign="top">Альтернативный временный каталог (редко используется)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/you</code></td>
      <td align="left" valign="top">Ваш начальный каталог</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/usr/you/bin</code></td>
      <td align="left" valign="top">Ваши собственные программы</td>
     </tr>
    </table>
    <p>Таблица 2.1: <strong>Интересные каталоги (см. также <code>hier(7)</code>)</strong></p>
    <empty-line/>
    <p>Каталог <code>/bin</code> вам уже известен: в нем находятся основные программы типа <code>who</code> или <code>ed</code>.</p>
    <p>Каталог <code>/dev</code> (device — устройства) мы обсудим в следующем разделе.</p>
    <p>Каталог <code>/etc</code> (et cetera — и т.д.) также уже вам встречался ранее. В нем находится различная служебная информация, например файл паролей, и некоторые системные программы, такие, как <code>/etc/getty</code>, которая инициирует связь с терминалом для команды <code>/bin/login</code>, <code>/etc/rc</code> — это файл команд, выполняющихся после раскрутки системы. В файле <code>/etc/group</code> содержатся сведения о составе всех групп.</p>
    <p>Каталог <code>/lib</code> (library — библиотека) включает основные части компилятора языка Си, такие, как <code>/lib/cpp</code> — препроцессор Си, <code>/lib/libc.a</code> — библиотека стандартных функций Си.</p>
    <p>Каталог <code>/tmp</code> (temporaries — временное) представляет собой хранилище для временных файлов, создаваемых при выполнении программы.</p>
    <p>Например, когда вы вызываете редактор, он создает файл с именем типа <code>/tmp/e00512</code>, что позволяет иметь свою копию редактируемого файла, а не работать с оригиналом. Редактор мог бы, конечно, создать копию в вашем текущем каталоге, но есть причина для преимущественного использования <code>/tmp</code>: хотя это и маловероятно, в вашем каталоге уже мог присутствовать файл <code>e00512</code>. Далее каталог <code>/tmp</code> автоматически очищается при запуске системы, так что в случае системной аварии в вашем каталоге не появится ненужный файл. Часто каталог <code>/tmp</code> организуется на диске для обеспечения быстрого доступа к нему. Однако здесь возникает проблема: если сразу несколько программ создают файлы в каталоге <code>/tmp</code>, их файлы могут перепутаться. Именно поэтому редактор <code>ed</code> выбирает особое имя; оно построено таким образом, чтобы никакая другая программа не могла выбрать то же имя для временного файла. В гл. 5 и 6 будет показан способ достижения этого.</p>
    <p>Каталог <code>/usr</code> называется файловой системой пользователей, хотя он может быть мало связан с файлами настоящих пользователей системы. На своей машине мы используем исходные каталоги <code>/usr/bwk</code> и <code>/usr/rob</code>, но у вас часть иерархии, начинающаяся с <code>/usr</code>, может быть другой. Независимо от того, находятся ли ваши файлы в каталоге, вложенном в <code>/usr</code>, вы всегда найдете в нем что-нибудь интересное (если нет местной специфики). Так же, как и в каталоге <code>/</code>, здесь есть каталоги с именами <code>/usr/bin</code>, <code>/usr/lib</code> и <code>/usr/tmp</code>. Эти каталоги имеют назначение, сходное со своими тезками в каталоге <code>/</code>, но содержат программы, менее критичные для системы. Например, программа <code>nroff</code> обычно находится в <code>/usr/bin</code>, а не в <code>/bin</code>, библиотеки компилятора с Фортрана располагаются в <code>/usr/lib</code>. Правда, "критичными" для разных систем считаются разные программы. Некоторые системы, такие, как широко распространенная седьмая версия, все программы хранят в <code>/bin</code>, не имея дела с <code>/usr/bin</code>. В других системах каталог <code>/usr/bin</code> разбивается на два каталога в зависимости от частоты использования.</p>
    <p>Кроме того, в <code>/usr</code> есть каталог <code>/usr/adm</code> со справочной информацией и <code>/usr/dict</code>, содержащий небольшой словарь (см. <code>spell(1)</code>). Диалоговое справочное руководство хранится в <code>/usr/man</code> (см. в качестве примера <code>/usr/man/man1/spell.1</code>). Если в вашей системе имеются исходные тексты, вы, вероятно, найдете их в <code>/usr/src</code>.</p>
    <p>Вероятно, целесообразно потратить немного времени на исследование файловой системы, особенно каталога <code>/usr</code>, чтобы вам было понятно, как она устроена и где что можно найти.</p>
   </section>
   <section>
    <title>
     <p>2.7 Файлы устройств</p>
    </title>
    <p>В нашем кратком обзоре мы пропустили каталог <code>/dev</code> по той причине, что файлы в нем дают хорошее общее представление о файлах вообще. Как можно догадаться по его названию, этот каталог содержит файлы устройств ("device" — устройство).</p>
    <p>К привлекательным чертам системы UNIX относится форма ее работы с периферийными устройствами: дисками, магнитными лентами, принтерами, терминалами и т.п. Вместо того чтобы иметь специальные системные программы, например программу чтения с магнитной ленты, достаточно создать файл с именем <code>/dev/mt0</code> (опять-таки местные соглашения могут различаться). В ядре обращения к этому файлу преобразуются в машинные команды обращения к магнитной ленте, как если бы программа читала <code>/dev/mt0</code>, выдавая содержимое магнитной ленты, подключенной к устройству. Например, команда</p>
    <p><code>$ cp /dev/mt0 junk</code></p>
    <p>копирует содержимое магнитной ленты в файл <code>junk</code>. Команда <code>cp</code> не имеет понятия о специфике файла <code>/dev/mt0</code>; для нее он является обычным файлом, т.е. просто последовательностью байтов.</p>
    <p>Файлы устройств в чем-то подобны зверинцу, где каждая особь чем-нибудь отличается от остальных, но основные характеристики применимы ко всем. Ниже приведен сокращенный список нашего каталога <code>/dev</code>:</p>
    <p><code>$ ls -l /dev</code></p>
    <p><code>crw--w--w- 1 root  0,  0 Sep 27 23:09 console</code></p>
    <p><code>crw-r--r-- 1 root  3,  1 Sep 27 14:37 fcmem</code></p>
    <p><code>crw-r--r-- 1 root  3,  0 May  6  1981 mem</code></p>
    <p><code>brw-rw-rw- 1 root  1, 64 Aug 24 17:41 mt0</code></p>
    <p><code>crw-rw-rw- 1 root  3,  2 Sep 28 02:03 null</code></p>
    <p><code>crw-rw-rw- 1 root  4, 64 Sep  9 15:42 rmt0</code></p>
    <p><code>brw-r----- 1 root  2,  0 Sep  8 08:07 rp00</code></p>
    <p><code>brw-r----- 1 root  2,  1 Sep 27 23:09 rp01</code></p>
    <p><code>crw-r----- 1 root 13,  0 Apr 12  1983 rrp00</code></p>
    <p><code>crw-r----- 1 root 13,  1 Jul 28 15:18 rrp01</code></p>
    <p><code>crw-rw-rw- 1 root  2,  0 Jul  5 08:04 tty</code></p>
    <p><code>crw--w--w- 1 root  1,  0 Sep 28 02:38 tty0</code></p>
    <p><code>crw--w--w- 1 root  1,  1 Sep 27 23:09 tty1</code></p>
    <p><code>crw--w--w- 1 root  1,  2 Sep 27 17:33 tty2</code></p>
    <p><code>crw--w--w- 1 root  1,  3 Sep 27 18:48 tty3</code></p>
    <p><code>$</code></p>
    <p>Первое, что здесь бросается в глаза, это то, что вместо количества байтов указывается пара небольших целых чисел, а в первой позиции прав доступа используется <code>'b'</code> или <code>'c'</code>. В таком виде команда <code>ls</code> выдает информацию из индексного дескриптора для файла устройств, но не для обычного файла. Обычному файлу предназначен хранимый в индексном дескрипторе список блоков памяти диска, в которых находится содержимое файла. В случае же файла устройств индексный дескриптор содержит внутреннее имя устройства, включающее его тип (<emphasis>символьное</emphasis> <code>с</code> или <emphasis>блочное</emphasis> <code>b</code>) и пару чисел, называемых верхним и нижним числами устройства. К блочным устройствам относятся диски и магнитные ленты, а все остальное: терминалы, принтеры, линии сетевой связи и т.п. — к символьным. Верхнее число устройства обозначает его тип, а нижнее характеризует различные экземпляры устройств одного типа. Например, <code>/dev/tty0</code> и <code>/dev/tty1</code> — это два порта одного контроллера терминала, поэтому они имеют одно и то же верхнее число и различные нижние числа.</p>
    <p>Файлы для дисков обычно именуются в соответствии с тем вариантом оборудования, которое представлено в системе. Файлы <code>/dev/rp00</code> и <code>/dev/rp01</code> названы так потому, что в системе используются дисковые накопители DEC RP06. Есть только один дисковый накопитель, логически поделенный на две файловые системы. Если бы существовал еще один накопитель, связанные с ним файлы имели бы имена <code>/dev/rp10</code> и <code>/dev/rp11</code>. Первая цифра обозначает номер накопителя, а вторая показывает, какая его часть используется.</p>
    <p>У вас может возникнуть вопрос: почему существует несколько дисковых файлов устройств, а не одно? Исторически так сложилось (и для удобства поддержания), что файловая система была разделена на подсистемы. Файлы в подсистеме доступны через каталог главной системы. Программа <code>/etc/mount</code> показывает соответствие между файлами устройств и каталогами:</p>
    <p><code>$ /etc/mount</code></p>
    <p><code>rp01 on /usr</code></p>
    <p><code>$</code></p>
    <p>В нашем случае каталог root находится на <code>/dev/rp00</code> (хотя команда <code>/etc/mount</code> об этом не сообщает), а файловая система пользователей, т.е. файлы из каталога <code>/usr</code> и вложенных каталогов, находится на <code>/dev/rp01</code>.</p>
    <p>Каталог <code>/root</code> должен быть доступен системе для выполнения команд. Каталоги <code>/bin</code>, <code>/dev</code> и <code>/etc</code> всегда находятся в корневом каталоге, поскольку при запуске системы доступны только файлы корневого каталога, а такие, как <code>/bin/sh</code>, необходимы для работы. Во время раскрутки системы все файловые системы проверяются на целостность (см. <code>icheck(8)</code> или <code>fsck(8)</code>) и подключаются к корню иерархии файлов. Эта операция подключения называется присоединением и является программистским эквивалентом операции установки пакета дисков на накопитель; обычно она выполняется только суперпользователем. После присоединения <code>/dev/rp01</code> в качестве <code>/usr</code> файлы пользователей становятся доступными, как если бы они были частью корневого каталога.</p>
    <p>Для обычного пользователя детали операции присоединения подсистемы файлов представляют мало интереса, но здесь есть несколько моментов, относящихся к нашей теме. Во-первых, поскольку подсистемы файлов могут быть присоединены и отсоединены, недопустимо устанавливать связь с файлом из другой подсистемы. Например, нельзя связать программы из общего каталога <code>/bin</code> с какими-то файлами из каталогов <code>/bin</code> пользователей, поскольку <code>/usr</code> находится в иной подсистеме файлов, чем <code>/bin</code>:</p>
    <p><code>$ ln /bin/mail /usr/you/bin/m</code></p>
    <p><code>ln: Cross-device link</code></p>
    <p><code>$</code></p>
    <p>Проблема могла возникнуть и потому, что значения индексных дескрипторов иногда совпадают в различных файловых системах.</p>
    <p>Далее, каждая подсистема ограничена по размеру (числу доступных блоков для файлов) и числу индексных дескрипторов. Если подсистема заполнена, то невозможно расширять файлы в такой системе, пока не будет добавлено какое-то пространство. Команда <code>df</code> ("disc free space" — свободное пространство диска) выдает сообщение о доступном пространстве в присоединенной подсистеме файлов:</p>
    <p><code>$ df</code></p>
    <p><code>/dev/rp00 1989</code></p>
    <p><code>/dev/rp01 21257</code></p>
    <p>В каталоге <code>/usr</code> имеется 21257 свободных блоков. Достаточно ли этого пространства или наступил кризис, зависит от того, как система используется; в одних случаях требуется больше свободного пространства, в других — меньше. Кстати, из всех команд <code>df</code>, вероятно, обеспечивает наибольшее разнообразие в формате вывода. Результат действия вашей команды <code>df</code> может выглядеть совершенно иначе.</p>
    <p>Рассмотрим теперь некоторые более общие понятия. При входе в систему вы устанавливаете связь вашего терминала с системой и, значит, получаете в каталоге <code>/dev</code> файл, через который передаются вводимые и принимаемые вами символы. Команда <code>tty</code> сообщает, какой терминал вы используете:</p>
    <p><code>$ whoami</code></p>
    <p><code>you tty0 Sep 28 01:02</code></p>
    <p><code>$ tty</code></p>
    <p><code>/dev/tty0</code></p>
    <p><code>$ ls -l /dev/tty0</code></p>
    <p><code>crw--w--w- 1 you 1, 12 Sep 28 02:40 /dev/tty0</code></p>
    <p><code>$ date &gt;/dev/tty0</code></p>
    <p><code>Wed Sep 28 02:40:51 EDT 1983</code></p>
    <p><code>$</code></p>
    <p>Заметьте, что вы владелец устройства и только у вас есть право на чтение с него. Иными словами, никто не может непосредственно читать вводимые вами символы, но выводить на ваш терминал может любой. Во избежание этого можно изменить права доступа к устройству, запретив тем самым другим использовать программу <code>write</code> для прямой записи или просто воспользоваться командой <code>mesg</code>.</p>
    <p><code>$ mesg n </code>Запретим сообщения</p>
    <p><code>$ ls -l /dev/tty0</code></p>
    <p><code>crw--w---- 1 you 1, 12 Sep 28 02:41 /dev/tty0</code></p>
    <p><code>$ mesg y </code>Разрешим</p>
    <p><code>$ ls -l /dev/tty0</code></p>
    <p><code>crw--w--w- 1 you 1, 12 Sep 28 02:42 /dev/tty0</code></p>
    <p><code>$</code></p>
    <p>Часто бывает удобно использовать имя для ссылки на применяемый терминал, но трудно определить, каково имя вашего терминала. Имя устройства <code>/dev/tty</code> является синонимом имени терминала, с которого вы вошли в систему, с каким бы терминалом вы ни работали на самом деле:</p>
    <p><code>$ date &gt;/dev/tty</code></p>
    <p><code>Wed Sep 28 02:42:23 EDT 1983</code></p>
    <p><code>$</code></p>
    <p>Имя <code>/dev/tty</code> особенно полезно, если программе необходимо начать диалог с пользователем, в то время когда ее стандартный входной и выходной потоки связаны с файлами, а не с терминалом. Команда <code>crypt</code> является одной из команд, использующих имя <code>/dev/tty</code>. "Открытый" текст поступает из стандартного входного потока, а зашифрованная информация направляется в стандартный выходной поток, поэтому команда <code>crypt</code> читает ключ для шифрования с <code>/dev/tty</code>:</p>
    <p><code>$ crypt &lt;cleartext &gt;cryptedtext</code></p>
    <p><code>Enter key:</code> Введите ключ шифрования</p>
    <p><code>$</code></p>
    <p>В данном примере имя <code>/dev/tty</code> используется неявно, но все-таки используется. Если бы команда <code>crypt</code> читала ключ из стандартного входного потока, она бы прочла первую строку из файла <code>cleartext</code>. Вместо этого она открывает файл <code>/dev/tty</code>, отключает автоматическое эхо вводимых символов, чтобы ваш ключ не появился на экране, и читает ключ. В гл. 5 и 6 приводится несколько других примеров использования <code>/dev/tty</code>.</p>
    <p>Иногда вы хотите запустить программу, но вам не важен результат ее выполнения. Например, вы могли уже ознакомиться с сегодняшними новостями и не желаете читать их еще раз. Переключение вывода команды <code>news</code> в файл <code>/dev/null</code> приведет к игнорированию выходного потока:</p>
    <p><code>$ news &gt;/dev/null</code></p>
    <p><code>$</code></p>
    <p>Информация, направляемая в <code>/dev/null</code>, просто пропадает, а программы, читающие из этого файла, сразу получают символ конца файла, поскольку программа чтения всегда возвращает 0 прочитанных байтов.</p>
    <p>Обычно файл <code>/dev/null</code> используют, чтобы отказаться от стандартного выходного потока и сделать видимыми диагностические сообщения. Например, команда <code>time</code> (<code>time(1)</code>) сообщает об использованном программой процессорном времени. Результат выдается в стандартный поток диагностики, так что можно хронометрировать команды, производящие преобразование входного потока в выходной, переключая стандартный выходной поток в файл <code>/dev/null</code>:</p>
    <p><code>$ ls -l /usr/diet/words</code></p>
    <p><code>-r--r--r-- 1 bin 196513 Jan 20 1979 /usr/dict/words</code></p>
    <p><code>$ time grep e /usr/dict/words/ &gt;/dev/null</code></p>
    <p><code>real 13.0</code></p>
    <p><code>user  9.0</code></p>
    <p><code>sys   2.7</code></p>
    <p><code>$ time egrep e /usr/dict/words &gt;/dev/null</code></p>
    <p><code>real  8.0</code></p>
    <p><code>user  3.9</code></p>
    <p><code>sys   2.8</code></p>
    <p><code>$</code></p>
    <p>Команда <code>time</code> выдает прошедшее календарное время, время процессора, затраченное программой, и время процессора, затраченное ядром системы для выполнения запросов программы. Команда <code>egrep</code> — это мощный вариант команды grep, который мы обсудим в гл. 4; она выполняется почти в два раза быстрее команды grep при просмотре больших файлов. Если бы выдача команд <code>egrep</code> или <code>grep</code> не была переключена в <code>/dev/null</code> или текущий файл, пришлось бы ждать, пока сотни тысяч символов "пробегут" на экране, прежде чем появятся нужные нам временные характеристики.</p>
    <cite>
     <subtitle>Упражнение 2.9</subtitle>
     <p>Познакомьтесь с другими файлами каталога <code>/dev</code>, прочитав разд. 4 справочного руководства. В чем состоит разница между <code>/dev/mt0</code> и <code>/dev/rmt0</code>? Прокомментируйте возможную пользу применения вложенных каталогов в <code>/dev</code> для дисков, магнитных лент и т.п.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 2.10</subtitle>
     <p>Магнитные ленты, записанные в других системах, обычно имеют другие размеры блоков, такие, как 800 байт — десятикратный образ перфокарты из 80 символов, но устройство <code>/dev/mt0</code> предполагает блоки из 512 байт. Обратитесь к команде <code>dd</code> (<code>dd(1)</code>), чтобы узнать, как читать такую ленту.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 2.11</subtitle>
     <p>Почему <code>/dev/tty</code> не является просто связью с терминалом, с которого вы вошли в систему? Что бы произошло, если бы права доступа для него были <code>rw--w--w-</code>, как на вашем терминале?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 2.12</subtitle>
     <p>Как работает <code>write(1)</code>? Подсказка: см. в <code>utmp(5)</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 2.13</subtitle>
     <p>Как узнать, использует ли человек терминал в данный момент?</p>
    </cite>
    <cite>
     <subtitle>Историческая и библиографическая справка</subtitle>
     <p>Файловой системе посвящена часть статьи К. Томпсона "UNIX implementation" (BSTJ, July, 1978). Статья Д. Ритчи ""The evolution of the UNIX time-sharing system" (Symposium on Language Design and Programming Methodology", Sydney, Australia, Sept., 1979) содержит завораживающее описание того, как разрабатывалась и была реализована на исходной PDP-7 файловая система UNIX и как она приобрела нынешнюю форму.</p>
     <p>При создании файловой системы UNIX были заимствованы некоторые идеи из системы файлов МАЛТИКС. Содержательное описание последней содержится в книге И. Органика "The MULTICS System: An Examination of its Structure" (MIT Press, 1972).</p>
     <p>Статья Б. Морриса и К. Томпсона "Password security: a case history" посвящена интересным сравнениям механизмов паролей во многих системах. Ее можно найти в т. 2В справочного руководства программиста системы UNIX. В том же томе есть статья Д. Ритчи "On the security of UNIX", в которой поясняется, что безопасность системы в большей степени зависит от мер, принимаемых администрацией, чем от деталей таких программ, как <code>crypt</code>.</p>
    </cite>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 3</p>
    <p>Возможности интерпретатора <code>shell</code></p>
   </title>
   <section>
    <p>Интерпретатор <code>shell</code> — это наиболее важная программа для пользователей UNIX, быть может, за исключением вашего любимого текстового редактора. Она исполняет ваши запросы на запуск программ и занимает гораздо больше вашего времени, чем любая другая программа системы. Значительная часть настоящей главы и гл. 5 будут посвящены описанию возможностей интерпретатора. Основная мысль, к которой мы хотим подвести вас, состоит в том, что если вы научитесь работать с интерпретатором, то сможете достичь многого и без особого труда, не прибегая к традиционным языкам программирования типа Си.</p>
    <p>Как уже отмечалось, описание интерпретатора разделено на две части. В этой главе от простейших возможностей, показанных в гл. 1, мы перейдем к рассмотрению некоторых необычных, но широко используемых конструкций, таких, как метасимволы, кавычки, новые команды с переданными им аргументами, переменные <code>shell</code> и отдельные структуры управления. Все это понадобится вам для эффективной работы с интерпретатором. Материал гл. 5 более сложный. Изучив его, вы сможете писать настоящие программы на языке <code>shell</code> и даже предоставлять их другим пользователям. Такое деление темы, конечно, во многом произвольно, поэтому мы рекомендуем вам прочитать обе главы.</p>
   </section>
   <section>
    <title>
     <p>3.1 Структура командной строки</p>
    </title>
    <p>Прежде чем продолжить рассмотрение, нужно уточнить, что такое команда и как она интерпретируется <code>shell</code>. Этот раздел содержит более формальное описание и некоторую информацию об основных возможностях интерпретатора, описанных в первой главе.</p>
    <p>Самая простая команда состоит из одного слова, обычно имени файла, предназначенного для выполнения (позднее вы познакомитесь с другими типами команд):</p>
    <p><code>$ who</code> Выполняем файл /bin/who</p>
    <p><code>you tty2 Sep 28 07:51</code></p>
    <p><code>jpl tty4 Sep 28 08:32</code></p>
    <p><code>$</code></p>
    <p>Команда, как правило, завершается символом перевода строки, но может завершаться и точкой с запятой:</p>
    <p><code>$ date;</code></p>
    <p><code>Wed Sep 28 09:07:15 EDT 1983</code></p>
    <p><code>$ date; who</code></p>
    <p><code>Wed Sep 28 09:07:23 EDT 1983</code></p>
    <p><code>you tty2 Sep 28 07:51</code></p>
    <p><code>jpl tty4 Sep 28 08:32</code></p>
    <p><code>$</code></p>
    <p>Однако выполнение команды не начнется, пока вы не нажмете клавишу <emphasis>RETURN</emphasis>. Обратите внимание на то, что интерпретатор выдает только одно приглашение после нескольких команд, но если не учитывать этого, то ввод</p>
    <p><code>$ date; who</code></p>
    <p>идентичен вводу двух команд в разных строках. В частности, команда <code>who</code> не будет выполняться до завершения <code>date</code>. Попробуйте послать результат выполнения этих команд по программному каналу:</p>
    <p><code>$ date; who | wc</code></p>
    <p><code>Wed Sep 28 09: 08:48 EDT 1983</code></p>
    <p><code>2 10 60</code></p>
    <p><code>$</code></p>
    <p>Возможно, вы получите не то, что ожидали, поскольку только результат команды <code>who</code> передается команде <code>wc</code>. При связывании <code>who</code> и <code>wc</code> через программный канал образуется единая команда, называемая конвейером, которая выполняется после <code>date</code>. В процессе разбора командной строки <code>shell</code> считает приоритет операции <code>'|'</code> выше, чем операции <code>';'</code>. Для группирования команд следует использовать скобки:</p>
    <p><code>$ (date; who)</code></p>
    <p><code>Wed Sep 28 09:11:09 EDT 1983</code></p>
    <p><code>you tty2 Sep 28 07:51</code></p>
    <p><code>jpl tty4 Sep 28 08:32</code></p>
    <p><code>$ (date; who) | wc</code></p>
    <p><code>3 16 89</code></p>
    <p><code>$</code></p>
    <p>Результат выполнения команд <code>date</code> и <code>who</code> конкатенируется в один поток, который можно передать по программному каналу.</p>
    <p>Информацию, поступающую по программному каналу, можно с помощью команды <code>tee</code> собрать и поместить в файл (но не в другой программный канал). Команда <code>tee</code> является частью интерпретатора <code>shell</code>, но тем не менее удобна и при манипулировании программными каналами. Ее можно использовать для сохранения промежуточного результата в файле:</p>
    <p><code>$ (date; who) | tee save | wc</code></p>
    <p><code>3 16 89 </code>Результат команды wc</p>
    <p><code>$ cat save</code></p>
    <p><code>Wed Sep 28 09:13:22 EDT 1983</code></p>
    <p><code>you tty2 Sep 28 07:51</code></p>
    <p><code>jpl tty4 Sep 28 08:32</code></p>
    <p><code>$ wc &lt;save</code></p>
    <p><code>3 16 48</code></p>
    <p><code>$</code></p>
    <p>Команда <code>tee</code> переписывает свой входной поток в поименованный файл (или файлы), а из него — точно так же без изменений в выходной поток, поэтому wc получает те же самые данные, как если бы команда <code>tee</code> не присутствовала в конвейере.</p>
    <p>В качестве еще одного символа, завершающего команду, применяют амперсанд (<code>&amp;</code>). Действие его аналогично действию символа перевода строки и точки с запятой, но он еще и указывает интерпретатору, что не нужно ждать завершения команды. Обычно <code>&amp;</code> используется для запуска фоновых, долго выполняющихся команд, в то время как вы продолжаете вводить новые команды в диалоге:</p>
    <p><code>$ long-running-command &amp;</code></p>
    <p><code>5273 </code>Номер процесса длительной команды</p>
    <p><code>$    </code>Приглашение появляется сразу</p>
    <p>Имея возможность группировать команды, получаем некоторые интересные способы применения фоновых процессов. Команда <code>sleep</code> ожидает указанное число секунд, прежде чем закончить свое выполнение:</p>
    <p><code>$ sleep 5</code></p>
    <p><code>$ </code>Проходит 5 секунд до появления приглашения</p>
    <p><code>$ (sleep 5; date) &amp; date</code></p>
    <p><code>5278</code></p>
    <p><code>Wed Sep 28 09:18:20 EDT 1983   </code>Результат второй команды date</p>
    <p><code>$ Wed Sep 28 09:18:25 EDT 1983 </code>Появляется приглашение, затем</p>
    <p>через 5 секунд дата</p>
    <p>Фоновый процесс начинается, но сразу "засыпает"; тем временем вторая команда <code>date</code> выдает текущее время, а интерпретатор — приглашение для ввода новой команды. Пятью секундами позже прекращается выполнение команды <code>sleep</code>, и первая команда <code>date</code> выдает новое время. Трудно представить на бумаге истечение времени, поэтому вам следует попытаться самостоятельно реализовать этот пример. (Разница между двумя значениями времени может и не равняться в точности 5 с, в зависимости от загруженности машины и по ряду других причин.) Это удобный способ отложить запуск команды на будущее; рассмотрите также в качестве удобного механизма такой пример:</p>
    <p><code>$ (sleep 300; echo Чай готов) &amp;</code> Чай будет готов через 5 минут</p>
    <p><code>5291</code></p>
    <p><code>$</code></p>
    <p>(Если в строке, следующей за командой <code>echo</code>, есть символ <emphasis>ctl-g</emphasis>, то при появлении ее на экране зазвонит звонок.) В этих примерах нужны скобки, так как приоритет <code>'&amp;'</code> выше, чем у <code>';'</code>.</p>
    <p>Символ <code>&amp;</code> может завершать команды, а поскольку конвейеры являются командами, в скобках для запуска конвейеров как фоновых процессов нет необходимости, поэтому</p>
    <p><code>$ pr файл | lpr &amp;</code></p>
    <p>позволяет выдать файл на печатающее устройство, не ожидая окончания выполнения команды. Использование скобок дает тот же эффект, но требует введения большего числа символов:</p>
    <p><code>$ (pr файл | lpr ) &amp;</code> To же, что и в предыдущем примере</p>
    <p>Большинство команд допускает наличие аргументов в командной строке, таких, как файл в предыдущем примере (аргумент команды <code>pr</code>). Аргументами служат слова, разделенные пробелами и символами табуляции, которые обычно именуют файлы, предназначенные для обработки командой. Однако они рассматриваются просто как строки, и программа может интерпретировать их любым подходящим для нее способом. Например, команда pr допускает имена файлов, которые нужно напечатать, команда <code>echo</code> посылает эхо своих аргументов без всякой интерпретации, а первый аргумент команды grep специфицирует строку-шаблон для поиска. И конечно, многие команды имеют необязательные параметры (флаги), задаваемые аргументами, начинающимися со знака “<code>-</code>”.</p>
    <p>Различные специальные символы, интерпретируемые <code>shell</code>, такие, как <code>&lt;</code>, <code>&gt;</code>, <code>|</code>, <code>;</code> и <code>&amp;</code>, не являются аргументами команд, запускаемых интерпретатором. Они управляют самим процессом запуска. Например,</p>
    <p><code>$ echo Hello &gt; junk</code></p>
    <p>требует, чтобы интерпретатор запустил команду <code>echo</code> с одним аргументом <code>Hello</code> и поместил выходной поток в файл <code>junk</code>. Строка <code>&gt; junk</code> не является аргументом команды <code>echo</code>; она интерпретируется <code>shell</code>, и <code>echo</code> никогда ее "не увидит". На самом деле, данная строка может и не быть последней в командной строке:</p>
    <p><code>$ &gt; junk echo Hello</code></p>
    <p>Это идентичный запуск, хотя и менее очевидный.</p>
    <cite>
     <subtitle>Упражнение 3.1</subtitle>
     <p>В чем состоит различие между следующими командами?</p>
     <p><code>$ cat file | pr</code></p>
     <p><code>$ pr &lt;file</code></p>
     <p><code>$ pr file</code></p>
     <p>(С течением времени операция переключения <code>&lt;</code> потеряла свою связь с программными каналами; "<code>cat file |</code>" считается более естественным, чем "<code>&lt; file</code>".)</p>
    </cite>
   </section>
   <section>
    <title>
     <p>3.2 Метасимволы</p>
    </title>
    <p>Интерпретатор распознает еще ряд символов как специальные. Наиболее часто используется звездочка <code>*</code>, указывающая, что нужно искать в каталоге имена файлов, у которых вместо <code>*</code> может быть любая последовательность символов. Например,</p>
    <p><code>$ echo *</code></p>
    <p>есть не что иное, как некое подобие команды <code>ls</code>. В гл. 1 мы не отметили, что во избежание проблем с именами <code>'.'</code> и <code>'..'</code>, которые присутствуют в любом каталоге, символы подстановки в именах файлов нельзя применять к именам файлов, начинающимся с точки. Правило таково: символы подстановки в именах файлов действуют на имена файлов, начинающихся с точки, только в том случае, если точка явно задана в шаблоне. Как обычно, "рассудительная" команда <code>echo</code> прояснит ситуацию:</p>
    <p><code>$ ls</code></p>
    <p><code>.profile</code></p>
    <p><code>junk</code></p>
    <p><code>temp</code></p>
    <p><code>$ echo *</code></p>
    <p><code>junk temp</code></p>
    <p><code>$ echo .*</code></p>
    <p><code>. .. .profile</code></p>
    <p><code>$</code></p>
    <p>Символы со специальным значением, подобные <code>*</code>, называются <emphasis>метасимволами</emphasis>. Существует множество метасимволов (в табл. 3.1 приводится их полный список, но некоторые символы мы обсудим только в гл. 5).</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>&gt;</code></td>
      <td align="left" valign="top"><code>prog &gt; file</code> — переключить стандартный выходной поток в файл</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>&gt;&gt;</code></td>
      <td align="left" valign="top"><code>prog &gt;&gt; file</code> — добавить стандартный выходной поток к файлу</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>&lt;</code></td>
      <td align="left" valign="top"><code>prog &lt; file</code> — извлечь стандартней выходной поток из файла</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>|</code></td>
      <td align="left" valign="top"><code>p1 | p2</code> — передать стандартный выходной поток <code>p1</code> как стандартный выходной поток для <code>p2</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>&lt;&lt;str</code></td>
      <td align="left" valign="top">"Документ здесь": стандартный выходной поток задается в последующих строках до строки, состоящей из одного символа <code>str</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>*</code></td>
      <td align="left" valign="top">Задает любую строку, состоящую из нуля или более символов, в имени файла</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>?</code></td>
      <td align="left" valign="top">Задает любой символ в имени файла</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>[ccc]</code></td>
      <td align="left" valign="top">Задает любой символ из <code>[ccc]</code> в имени файла (допустимы диапазоны, такие, как <code>0-9</code> или <code>a-z</code>)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>;</code></td>
      <td align="left" valign="top">Завершает команды: <code>p1; p2</code> — выполнить <code>p1</code>, затем <code>p2</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>&amp;</code></td>
      <td align="left" valign="top">Выполняет аналогичные функции, но не ждет окончания <code>p1</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>`...`</code></td>
      <td align="left" valign="top">Инициирует выполнение команд(ы) в <code>...</code>; <code>`...`</code> заменяется своим стандартным выводом</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>(...)</code></td>
      <td align="left" valign="top">Инициирует выполнение команд(ы) в <code>...</code> в порожденном <code>shell</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>{...}</code></td>
      <td align="left" valign="top">Инициирует выполнение команд(ы) в <code>...</code> в текущем вызове shell (используется редко)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$1, $2, ...</code></td>
      <td align="left" valign="top">Заменяются аргументами командного файла</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$var</code></td>
      <td align="left" valign="top">Значение переменной <code>var</code> в программе на языке <code>shell</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>${var}</code></td>
      <td align="left" valign="top">Значение <code>var</code>; исключает коллизии в случае конкатенации переменной с последующим текстом (см. также табл. 5.3)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\</code></td>
      <td align="left" valign="top"><code>\c</code> — использовать непосредственно символ <code>c</code>, <code>\</code> перевод строки отбрасывается</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>'...'</code></td>
      <td align="left" valign="top">Означает непосредственное использование</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>"..."</code></td>
      <td align="left" valign="top">Означает непосредственное использование, но после того, как <code>$</code>, <code>`...`</code> и <code>\</code> будут интерпретированы</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>#</code></td>
      <td align="left" valign="top">В начале слова означает, что вся остальная строка рассматривается как комментарий (но не в седьмой версии)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>var=value</code></td>
      <td align="left" valign="top">Присваивает <code>value</code> переменной <code>var</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>p1 &amp;&amp; p2</code></td>
      <td align="left" valign="top">Предписывает выполнить <code>p1</code>; в случае успеха выполнить <code>p2</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>p1 || p2</code></td>
      <td align="left" valign="top">Предписывает выполнить <code>p1</code>; в случае неудачи выполнить <code>p2</code></td>
     </tr>
    </table>
    <p>Таблица 3.1: <strong>Метасимволы <code>shell</code></strong></p>
    <empty-line/>
    <p>При таком количестве метасимволов интерпретатора необходимо иметь возможность экранировать специальный символ от интерпретации. Самый простой и надежный способ экранирования — заключить его в апострофы:</p>
    <p><code>$ echo '* * *'</code></p>
    <p><code>* * *</code></p>
    <p><code>$</code></p>
    <p>Можно также использовать кавычки <code>"..."</code>, но интерпретатор на самом деле "заглядывает" внутрь этих кавычек в поиске метасимволов <code>$</code>, <code>'...'</code> и <code>\</code>, так что не применяйте <code>"..."</code>, если только вам не требуется определенным образом обработать строку в кавычках.</p>
    <p>Еще одну возможность дает ввод обратной дробной черты перед каждым символом, который вы хотите закрыть от интерпретатора, например:</p>
    <p><code>$ echo \*\*\*</code></p>
    <p>Хотя строка <code>\*\*\*</code> не похожа на английское слово, в терминологии языка <code>shell</code> это слово, ибо им является любая последовательность символов, воспринимаемая интерпретатором как целое, включая даже пробелы, если они взяты в кавычки.</p>
    <p>Кавычки одного вида могут экранировать кавычки другого вида:</p>
    <p><code>$ echo "Don't do that!"</code></p>
    <p><code>Don't do that!</code></p>
    <p><code>$</code></p>
    <p>и могут не заключать в себе весь аргумент:</p>
    <p><code>$ echo x'*'y</code></p>
    <p><code>x*y</code></p>
    <p><code>$ echo '*'A'?'</code></p>
    <p><code>*А?</code></p>
    <p><code>$</code></p>
    <p>В последнем примере команда <code>echo</code> получает один аргумент, не содержащий апострофов, так как, сделав свое дело, апострофы исчезают. Строки в кавычках могут содержать символы строк:</p>
    <p><code>$ echo 'hello</code></p>
    <p><code>&gt; world'</code></p>
    <p><code>hello</code></p>
    <p><code>world</code></p>
    <p><code>$</code></p>
    <p>Символ <code>&gt;</code> является вторичным приглашением интерпретатора, которое выдается, если ожидается продолжение ввода для завершения команды. В этом примере апостроф в первой строке должен быть уравновешен другим апострофом. Вторичное приглашение интерпретатора хранится в переменной <code>PS2</code>; его можно изменить по своему вкусу.</p>
    <p>Во всех приведенных выше примерах экранирование специальных символов предохраняет их от интерпретации. Команда</p>
    <p><code>$ echo x*y</code></p>
    <p>выдает все имена файлов, начинающиеся с <code>x</code> и кончающиеся <code>y</code>. Как обычно, команда <code>echo</code> ничего "не знает" ни о файлах, ни о метасимволах; интерпретация <code>*</code>, если она требуется, осуществляется <code>shell</code>.</p>
    <p>Что произойдет, если ни один файл не будет соответствовать шаблону? Интерпретатор просто пропустит строку, как если бы она была взята в кавычки, а не выразит вам свое неудовольствие (как было принято в ранних версиях). Конечно, не следует рассчитывать на это свойство, но его можно использовать, чтобы узнать о существовании файлов, соответствующих шаблону:</p>
    <p><code>$ ls x*y</code></p>
    <p><code>x*y not found </code>Сообщение ls: таких файлов нет</p>
    <p><code>$ &gt;xyzzy      </code>Создать файл xyzzy</p>
    <p><code>$ ls x*y</code></p>
    <p><code>xyzzy         </code>Файл xyzzy соответствует x*y</p>
    <p><code>$ ls 'х*y'</code></p>
    <p><code>x*y not found </code>ls не интерпретирует *</p>
    <p><code>$</code></p>
    <p>Появление обратной дробной черты в конце строки требует продолжения строки, что является способом задать интерпретатору очень длинную строку:</p>
    <p><code>$ echo abc\</code></p>
    <p><code>&gt; def\</code></p>
    <p><code>&gt; ghi</code></p>
    <p><code>abcdefghi</code></p>
    <p><code>$</code></p>
    <p>Обратите внимание на то, что символ перевода строки отбрасывается, если перед ним стоит обратная дробная черта, но он остается, если взят в кавычки. Метасимвол <code>#</code> в программе на языке <code>shell</code> практически всюду используется в качестве комментария; если слово начинается с <code>#</code>, остаток строки игнорируется:</p>
    <p><code>$ echo hello#there</code></p>
    <p><code>hello</code></p>
    <p><code>$ echo hello # there</code></p>
    <p><code>hello # there</code></p>
    <p><code>$</code></p>
    <p>Символ <code>#</code> не присутствует в оригинальной седьмой версии, но имеет очень широкое распространение, и в дальнейшем мы будем им пользоваться.</p>
    <cite>
     <subtitle>Упражнение 3.2</subtitle>
     <p>Объясните результат выполнения команды</p>
     <p><code>$ ls .</code></p>
    </cite>
    <subtitle>Некоторые дополнительные сведения о команде <code>echo</code></subtitle>
    <p>Команда <code>echo</code> выдает заключительный символ перевода строки, даже если на это нет явного запроса. Разумной и, возможно, более корректной была бы такая реализация команды <code>echo</code>, при которой вывод соответствовал бы только запросу. Добиться этого легко, если потребовать от интерпретатора выдачи приглашения:</p>
    <p><code>$ правильное эхо введенная команда:</code></p>
    <p><code>Введенная команда: $</code> Нет завершающего перевода строки</p>
    <p>Однако при таком решении в самой распространенной ситуации, когда перевод строки нужен, он не подразумевается по умолчанию и требует дополнительного ввода:</p>
    <p><code>$ правильное эхо 'Привет!</code></p>
    <p><code>&gt;'</code></p>
    <p><code>Привет!</code></p>
    <p><code>$</code></p>
    <p>Поскольку команда должна по умолчанию выполнять наиболее часто встречающееся действие, настоящее эхо автоматически добавляет перевод строки.</p>
    <p>Но как быть, если это нежелательно? В седьмой версии системы команда <code>echo</code> имеет единственный флаг <code>-n</code>, который подавляет последний символ перевода строки:</p>
    <p><code>$ echo -n Enter a command:</code></p>
    <p><code>Enter a command: $ </code>Приглашение на той же строке</p>
    <p><code>$ echo -</code></p>
    <p><code>-                  </code>Только - является специальным случаем</p>
    <p><code>$</code></p>
    <p>Существует одна маленькая хитрость в случае получения эха от <code>-n</code>, за которым должен следовать символ перевода строки:</p>
    <p><code>$ echo -n '-n</code></p>
    <p><code>&gt;'</code></p>
    <p><code>-n</code></p>
    <p><code>$</code></p>
    <p>Такое решение некрасиво, но эффективно, к тому же это довольно редкий случай.</p>
    <p>Другой подход принят в System V, где команда <code>echo</code> интерпретирует последовательность символов с обратной дробной чертой аналогично тому, как это делается в языке Си, а именно: <code>\b</code> обозначает "шаг назад", <code>\c</code> подавляет перевод строки (правда, здесь не очень точно воспроизведена конструкция Си):</p>
    <p><code>$ echo 'Введенная команда: \с'</code> Версия System V</p>
    <p><code>Введенная команда: $</code></p>
    <p>Хотя при подобном решении не возникает коллизий при получении эха от знака <code>"-"</code>, у него есть свои недостатки. Команда <code>echo</code> часто используется в качестве диагностического средства, а символ обратной дробной черты интерпретируется таким множеством программ, что участие в этом команды <code>echo</code> только вносит дополнительную путаницу.</p>
    <p>Итак, обе реализации команды <code>echo</code> имеют и положительные, и отрицательные стороны. Мы будем использовать вариант седьмой версии (<code>-n</code>), поэтому, если ваша команда <code>echo</code> выполняется по-другому, несколько приводимых ниже примеров потребуют незначительных изменений.</p>
    <p>Возникает еще один, философский, вопрос: что должна делать команда, если ей не передали аргументов, в частности, следует ли ей выдавать пустую строку или вообще ничего не предпринимать? Как вы уже знаете, все настоящие реализации команды выдают пустую строку, но в ранних версиях все было иначе. По этому поводу велись большие дебаты, а Д. МакИлрой привнес в них даже элемент мистицизма.</p>
    <cite>
     <subtitle>UNIX и Эхо</subtitle>
     <p>Жила-была в стране Нью-Джерси UNIX, прекрасная девушка, к которой приезжали издалека, чтобы полюбоваться ею. Ослепленные чистотой UNIX, все искали ее руки и сердца: одни — за изящество, другие — за изысканную вежливость, третьи — за проворность при выполнении самых изнурительных заданий. Была она от рождения столь великодушна и услужлива, что все женихи остались довольны ею, а ее многочисленное потомство распространилось во все концы земли.</p>
     <p>Сама природа покровительствовала UNIX и вторила ей более охотно, чем кому-либо из смертных. Простые люди поражались ее эхом, таким оно было точным и кристально чистым. Они не могли поверить, что ей отвечают те же леса и скалы, которые так искажают их собственные голоса. Когда один нетерпеливый пастушок попросил UNIX: "Пусть эхо ответит ничего", и она послушно открыла рот, эхо промолчало. "Зачем ты открываешь рот?" — спросил пастушок. — "Отныне никогда не открывай его, если эхо должно ответить ничего!" — и UNIX подчинилась.</p>
     <p>"Но я хочу совершенного исполнения, даже если эхо отвечает ничего," — потребовал другой, обидчивый, юноша, — "а никакого совершенного эха не получится при закрытом рте". Не желая обидеть никого из них, UNIX согласилась говорить разные "ничего" для нетерпеливого и обидчивого юношей. Она называла "ничего" для обидчивого как '<code>\n'</code>. Однако теперь, когда она говорила '<code>\n'</code>, на самом деле она не произносила ничего, поэтому ей приходилось открывать рот дважды: один раз, чтобы сказать <code>'\n'</code>, и второй раз, чтобы не сказать ничего. Это не понравилось обидчивому юноше, который тотчас сказал: "Для меня <code>'\n'</code> звучит, как настоящее "ничего", но когда ты открываешь рот второй раз, то все портишь. Возьми второе "ничего" назад". Услужливая UNIX согласилась отказаться от некоторых эхо и обозначила это как <code>'\c'</code>. С тех пор обидчивый юноша мог услышать совершенное эхо "ничего", если он задавал <code>'\n'</code> и <code>'\c'</code> вместе, но говорят, что он так и не услышал его, поскольку умер от излишеств в обозначениях.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 3.3</subtitle>
     <p>Предскажите, что сделает команда <code>grep</code> в каждом случае, а затем проверьте себя;</p>
     <p><code>grep \$     grep \\</code></p>
     <p><code>grep \\$    grep \\\\</code></p>
     <p><code>grep \\\\$  grep "\$"</code></p>
     <p><code>grep '\$'   grep '"$'</code></p>
     <p><code>grep '\'$   grep "$"</code></p>
     <p>Файл, состоящий из таких команд, послужит хорошим материалом для теста, если вы хотите поэкспериментировать.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 3.4</subtitle>
     <p>Как указать <code>grep</code>, что нужно найти шаблон, начинающийся с <code>'-'</code>? Почему взятие аргумента в кавычки не помогает? Подсказка: исследуйте флаг <code>-е</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 3.5</subtitle>
     <p>Рассмотрите команду</p>
     <p><code>$ echo */*</code></p>
     <p>Может ли она вывести все имена всех каталогов? В каком порядке появятся эти имена?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 3.6</subtitle>
     <p>(<emphasis>Хитрый вопрос</emphasis>.) Как ввести <code>/</code> в локальное имя файла (т.е. символ <code>/</code>, который не является разделителем компонентов в абсолютном имени)?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 3.7</subtitle>
     <p>Что произойдет в случае ввода команд <code>$ cat x y &gt;y</code> и <code>$ cat x &gt;&gt;x</code></p>
     <p>Подумайте, прежде чем броситься их выполнять.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 3.8</subtitle>
     <p>Если вы введете</p>
     <p><code>$ rm *</code></p>
     <p>почему команда <code>rm</code> не сможет предупредить вас, что вы собираетесь удалить все ваши файлы?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>3.3 Создание новых команд</p>
    </title>
    <p>Теперь, как мы обещали вам в гл. 1, рассмотрим создание новых команд из старых. Имея последовательность команд, которую придется многократно повторять, преобразуем ее для удобства в "новую" команду со своим именем и будем использовать ее как обычную команду. Чтобы быть точными, предположим, что нам предстоит часто подсчитывать число пользователей с помощью конвейера</p>
    <p><code>$ who | wc -l</code></p>
    <p>(см. гл. 1), и для этой цели нужна новая программа <code>nu</code>.</p>
    <p>Первым шагом должно быть создание обычного файла, содержащего <code>'who | wc -l'</code>. Можно воспользоваться вашим любимым редактором или проявить изобретательность:</p>
    <p><code>$ echo 'who | wc -l' &gt;nu</code></p>
    <p>(Что появится в файле <code>nu</code>, если не употреблять кавычки?)</p>
    <p>Как отмечалось в гл. 1, интерпретатор является точно такой же программой, как редактор, <code>who</code> или <code>wc</code>; он называется <code>sh</code>. А коль скоро это программа, ее можно вызвать и переключить ее входной поток. Так что запускаем интерпретатор с входным потоком, поступающим из файла <code>nu</code>, а не с терминала:</p>
    <p><code>$ who</code></p>
    <p><code>you tty2 Sep 28 07:51 rhh tty4 Sep 28 10:02</code></p>
    <p><code>moh tty5 Sep 28 09:38 ava tty6 Sep 28 10:17</code></p>
    <p><code>$ cat nu who | wc -l</code></p>
    <p><code>$ sh &lt; nu</code></p>
    <p><code>4</code></p>
    <p><code>$</code></p>
    <p>Результат получился таким же, каким бы он был при задании команды <code>who | wc -l</code> с терминала. Опять-таки, как и большинство программ, интерпретатор берет входной поток из файла, если он указан в качестве аргумента; вы с тем же успехом могли задать:</p>
    <p><code>$ sh nu</code></p>
    <p>Однако досадно вводить "<code>sh</code>" каждый раз; во всяком случае эта запись длиннее и создает различия между программами, написанными, например, на Си, и программами, написанными с помощью <code>shell</code>.<a l:href="#n9" type="note">[9]</a> Поэтому если файл предназначен для выполнения и если он содержит текст, то интерпретатор считает, что он состоит из команд. Такие файлы называются командными. Все, что вам нужно сделать, это объявить файл <code>nu</code> выполняемым, задав</p>
    <p><code>$ chmod + x nu</code></p>
    <p>а затем вы можете вызывать его посредством</p>
    <p><code>$ nu</code></p>
    <p>С этого момента те, кто используют файл nu, не смогут определить способ его создания.</p>
    <p>Способ, с помощью которого интерпретатор на самом деле выполняет <code>nu</code>, сводится к созданию нового процесса интерпретатора, как если бы вы задали</p>
    <p><code>$ sh nu</code></p>
    <p>Этот процесс-потомок называется порожденным интерпретатором, т.е. процессом интерпретатора, возбужденным вашим текущим интерпретатором. Но команда <code>sh nu</code> — это не то же самое, что <code>sh &lt; nu</code>, поскольку в первом случае стандартный входной поток все еще связан с терминалом. Пока команда <code>nu</code> выполняется только в том случае, если она находится в вашем текущем каталоге (при условии, конечно, что текущий каталог включен в <code>PATH</code>, а именно это мы и предполагаем с настоящего момента). Чтобы сделать команду nu частью вашего репертуара независимо от того каталога, с которым вы работаете, занесите ее в свой собственный каталог <code>bin</code> и добавьте <code>/usr/you/bin</code> к списку каталогов поиска:</p>
    <p><code>$ pwd /usr/you</code></p>
    <p><code>$ mkdir bin                 </code>Создать bin, если его еще не было</p>
    <p><code>$ echo $PATH                </code>Проверить Path, чтобы убедиться</p>
    <p><code>:/usr/you/bin:/bin:/usr/bin </code>Должно быть нечто похожее</p>
    <p><code>$ mv nu bin                 </code>Установить команду nu в bin</p>
    <p><code>$ ls nu</code></p>
    <p><code>nu not found                </code>Она действительно исчезла</p>
    <p>из текущего каталога</p>
    <p><code>$ nu</code></p>
    <p><code>4                           </code>Но интерпретатор ее находит</p>
    <p><code>$</code></p>
    <p>Конечно, ваша переменная <code>PATH</code> должна быть правильно определена в файле <code>.profile</code>, чтобы вам не приходилось переопределять ее при каждом входе в систему.</p>
    <p>Существуют и другие простые команды, которые вы можете адаптировать к среде по своему вкусу и создавать таким же способом. Нам показалось удобным иметь следующие команды:</p>
    <p>• <code>cs</code> для посылки подходящей последовательности специфических символов с целью очистки экрана вашего терминала (24 символа перевода строки — практически универсальное решение);</p>
    <p>• <code>what</code> для запуска <code>who</code> и <code>ps -а</code>, чтобы сообщить, кто работает в системе и что он делает;</p>
    <p>• <code>where</code> для вывода идентифицированного названия используемой системы UNIX. Это удобно, если вы постоянно работаете с несколькими версиями. (Установка <code>PS1</code> служит для подобной цели.)</p>
    <cite>
     <subtitle>Упражнение 3.9</subtitle>
     <p>Просмотрите каталоги <code>/bin</code> и <code>/usr/bin</code>, чтобы выяснить, как много команд являются в действительности командными файлами. Можно ли это сделать с помощью одной команды? Подсказка: посмотрите <code>file(1)</code>. Насколько точно предположение, основанное на длине файла?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>3.4 Аргументы и параметры команд</p>
    </title>
    <p>Хотя команда <code>nu</code>, как она задумывалась, удовлетворяет своему назначению, многие программы на языке <code>shell</code> могут обрабатывать аргументы, так что при их запуске можно задавать имена файлов и флаги.</p>
    <p>Допустим, вы хотите создать программу с именем <code>cx</code> для установки права доступа к файлу на выполнение, так что</p>
    <p><code>$ cx nu</code></p>
    <p>есть сокращенная запись для</p>
    <p><code>$ chmod +x nu</code></p>
    <p>Вы уже знаете почти все, чтобы это сделать. Вам нужен файл <code>cx</code>, содержимое которого суть</p>
    <p><code>chmod +x filename</code></p>
    <p>Единственное, что требуется выяснить — как сообщить команде <code>cx</code> имя файла, так как при каждом запуске <code>cx</code> оно будет иным.</p>
    <p>Если интерпретатор выполняет командный файл, то каждое вхождение <code>$1</code> заменяется первым аргументом, каждое вхождение <code>$2</code> — вторым и т.д. до <code>$9</code>. Поэтому если файл <code>cx</code> содержит строку</p>
    <p><code>chmod +x $1</code></p>
    <p>то при выполнении команды</p>
    <p><code>$ cx nu</code></p>
    <p>порожденный интерпретатор заменит "<code>$1</code>" на первый аргумент "<code>nu</code>". Рассмотрим всю последовательность операций:</p>
    <p><code>$ echo 'chmod +x $1' &gt;cx      </code>Вначале создадим cx</p>
    <p><code>$ sh cx сх                    </code>Сделать сам файл cx выполняемым</p>
    <p><code>$ echo echo Hi, there! &gt;hello </code>Приготовим тест</p>
    <p><code>$ hello                       </code>Попробуем</p>
    <p><code>hello: cannot execute</code></p>
    <p><code>$ cx hello                    </code>Сделаем файл выполняемым</p>
    <p><code>$ hello                       </code>Попробуем снова</p>
    <p><code>Hi, there!                    </code>Работает</p>
    <p><code>$ mv cx /usr/you/bin          </code>Установим команду cx</p>
    <p><code>$ rm hello                    </code>Уберем ненужное</p>
    <p><code>$</code></p>
    <p>Заметьте, что мы задали</p>
    <p><code>$ sh cx сх</code></p>
    <p>в точности так, как сделал бы автоматически интерпретатор, если бы <code>cx</code> была выполняемой и можно было бы задать</p>
    <p><code>$ cx сх</code></p>
    <p>А как быть, если нужно работать с несколькими аргументами, например, заставить программу <code>cx</code> воздействовать сразу на несколько файлов? Прямолинейное решение состоит в том, чтобы включить девять аргументов в командный файл:</p>
    <p><code>chmod +x $1 $2 $3 $4 $5 $6 $7 $8 $9</code></p>
    <p>(Это годится только для девяти аргументов, так как конструкция <code>$10</code> распознается как "первый аргумент, за которым следует 0"!) Если пользователь такого командного файла задаст меньше девяти аргументов, то недостающие окажутся пустыми строками. Это приведет к тому, что только настоящие аргументы будут переданы <code>chmod</code> порожденным интерпретатором. Такое решение, конечно, приемлемо, но не вполне корректно и не подходит для случая с числом аргументов более девяти.</p>
    <p>С учетом упомянутой выше трудности интерпретатор предоставляет сокращенную запись <code>$*</code>, означающую "все аргументы". В этом случае правильно определить <code>cx</code>:</p>
    <p><code>chmod +x $*</code></p>
    <p>что является эффективным при любом числе аргументов.</p>
    <p>Используя <code>$*</code> в своем репертуаре, вы можете создать некоторые полезные командные файлы, такие, как <code>lc</code> или <code>m</code>:</p>
    <p><code>$ cd /usr/you/bin</code></p>
    <p><code>$ cat lc</code></p>
    <p><code>#lc: подсчет числа строк в файлах</code></p>
    <p><code>wc -l $*</code></p>
    <p><code>$ cat m</code></p>
    <p><code>#m: точный способ послать почту</code></p>
    <p><code>mail $*</code></p>
    <p><code>$</code></p>
    <p>Обе команды можно осмысленно использовать и без аргументов. Если нет аргументов, <code>$*</code> будет пустым, и <code>wc</code> и <code>mail</code> вообще не получат никаких аргументов. С аргументами или без них команда вызывается правильно:</p>
    <p><code>$ lc /usr/you/bin/*</code></p>
    <p><code> 1 /usr/you/bin/cx</code></p>
    <p><code> 2 /usr/you/bin/lc</code></p>
    <p><code> 2 /usr/you/bin/m</code></p>
    <p><code> 1 /usr/you/bin/nu</code></p>
    <p><code> 2 /usr/you/bin/what</code></p>
    <p><code> 1 /usr/you/bin/where</code></p>
    <p><code> 9 total</code></p>
    <p><code>$ ls /usr/you/bin | lc</code></p>
    <p><code> 6</code></p>
    <p><code>$</code></p>
    <p>Эти и другие команды, описываемые в настоящей главе, являются командами пользователя, т.е. вы создаете их для себя и помещаете в свой каталог <code>/bin</code>, поэтому вряд ли они должны стать общедоступными. В гл. 5 мы исследуем вопрос создания общедоступных программ на языке <code>shell</code>.</p>
    <p>Аргументами командного файла не обязательно должны быть имена файлов. Рассмотрим в качестве примера поиск в каталоге, где хранится личный телефонный справочник. Если у вас есть файл с именем <code>/usr/you/lib/phone-book</code>, содержащий строки следующего вида:</p>
    <p><code>dial-a-joke      212-976-3838</code></p>
    <p><code>dial-a-prayer    212-246-4200</code></p>
    <p><code>dial santa       212-976-3636</code></p>
    <p><code>dow jones report 212-976-4141</code></p>
    <p>то для поиска в нем можно воспользоваться командой <code>grep</code>. (Ваш собственный каталог <code>lib</code> — хорошее хранилище таких частных баз данных.) Поскольку команда <code>grep</code> не определяет формат информации, можно искать имена, адреса, индексы или еще какие-нибудь нужные вам сведения. Составим справочную программу для каталога, которой дадим имя <code>411</code> по номеру одной из телефонных справочных служб:</p>
    <p><code>$ echo 'grep $* /usr/you/lib/phone-book' &gt; 411</code></p>
    <p><code>$ cx 411</code></p>
    <p><code>$ 411 joke</code></p>
    <p><code>dial-a-joke      212-976-3838</code></p>
    <p><code>$ 411 dial</code></p>
    <p><code>dial-a-joke      212-976-3838</code></p>
    <p><code>dial-a-prayer    212-246-4200</code></p>
    <p><code>dial santa       212-976-3636</code></p>
    <p><code>$ 411 'dow jones'</code></p>
    <p><code>grep: can't open jones</code> Что-то не так</p>
    <p><code>$</code></p>
    <p>Последний пример вскрывает потенциальную проблему: хотя <code>dow jones</code> представляет для команды <code>411</code> единый аргумент, он содержит пробел и уже не заключен в апострофы, поэтому порожденный интерпретатор, выполняющий команду <code>411</code>, преобразует его в два аргумента для <code>grep</code>, как если бы вы задали</p>
    <p><code>$ grep dow jones /usr/you/lib/phone-book</code></p>
    <p>что, очевидно, неверно.</p>
    <p>Один из возможных путей обойти эту проблему основан на том, как интерпретатор трактует кавычки. Хотя все, что заключено в <code>'...'</code>, не затрагивается, интерпретатор "заглядывает" внутрь <code>"..."</code> в поиске комбинаций с <code>$</code>, <code>\</code>, <code>`...`</code>. Поэтому если изменить команду 411 следующим образом:</p>
    <p><code>$ grep "$*" /usr/you/lib/phone-book</code></p>
    <p>то <code>$*</code> заменяется на аргументы, но команде <code>grep</code> передается как один аргумент, даже при наличии пробелов:</p>
    <p><code>$ 411 dow jones</code></p>
    <p><code>dow jones report 212-976-4141</code></p>
    <p><code>$</code></p>
    <p>Кстати, можно сделать с помощью флага <code>-y</code> команду <code>grep</code> (а значит, и <code>411</code>) независимой от использования строчных или прописных букв:</p>
    <p><code>$ grep -y pattern ...</code></p>
    <p>При наличии флага <code>-y</code> строчные буквы из шаблона могут сопоставляться с прописными буквами из входного потока. (Такой флаг есть в седьмой версии, но отсутствует в других системах.)</p>
    <p>Более подробно аргументы команд мы рассмотрим в гл. 5, но одно важное замечание необходимо сделать здесь. Аргумент <code>$0</code> — это имя выполняемой программы; в случае <code>cx $0</code> есть <code>"cx"</code>. Новое применение <code>$0</code> находит в реализации программ <code>2</code>, <code>3</code>, <code>4</code>, …, которые печатают свой выходной поток в несколько столбцов:</p>
    <p><code>$ who | 2</code></p>
    <p><code>drh tty0 Sep 28 21:23 cvw tty5 Sep 28 21:09</code></p>
    <p><code>dmr tty6 Sep 28 21:10 scj tty7 Sep 28 22:11</code></p>
    <p><code>you tty9 Sep 28 23:00 jib ttyb Sep 28 19:58</code></p>
    <p><code>$</code></p>
    <p>Реализация команд <code>2</code>, <code>3</code>, … идентична. По существу, они являются связями с одним файлом:</p>
    <p><code>$ ln 2 3; ln 2 4; ln 2 5; ln 2 6</code></p>
    <p><code>$ ls -l [1-9]</code></p>
    <p><code>167222 -rwxrwxrwx 5 you 51 Sep 28 23:21 2</code></p>
    <p><code>167222 -rwxrwxrwx 5 you 51 Sep 28 23:21 3</code></p>
    <p><code>167222 -rwxrwxrwx 5 you 51 Sep 28 23:21 4</code></p>
    <p><code>167222 -rwxrwxrwx 5 you 51 Sep 28 23:21 5</code></p>
    <p><code>167222 -rwxrwxrwx 5 you 51 Sep 28 23:21 6</code></p>
    <p><code>$ ls /usr/you/bin | 5</code></p>
    <p><code>2    3     4    411   5</code></p>
    <p><code>6    cx    lc   m     nu</code></p>
    <p><code>what where</code></p>
    <p><code>$ cat 5</code></p>
    <p><code># 2, 3, ...: печать в n столбцов</code></p>
    <p><code>pr -$0 -t -11 $*</code></p>
    <p><code>$</code></p>
    <p>Флаг <code>-t</code> убирает заголовки в начале страницы, а флаг <code>-ln</code> устанавливает размер страницы равным <code>n</code> строк. Имя программы становится числом столбцов, т.е. аргументов для команды pr, так что выходной поток печатается строками по несколько столбцов, число которых определено аргументом <code>$0</code>.</p>
   </section>
   <section>
    <title>
     <p>3.5 Результат выполнения программы в качестве аргумента</p>
    </title>
    <p>Теперь перейдем от аргументов команд для командного файла к порождению аргументов. Конечно, расширение имен файлов с помощью метасимволов, подобных <code>*</code>, является наиболее типичным способом порождения аргументов (иным, чем их явное задание), но столь же хорошим способом представляется и выполнение программы. Результат выполнения любой программы можно использовать в командной строке, заключив ее вызов в символы слабого ударения <code>`...`</code>:</p>
    <p><code>$ echo At the tone the time will be `date`.</code></p>
    <p><code>At the tone the time will be Thu Sep 29 00:02:15 EDT 1983.</code></p>
    <p><code>$</code></p>
    <p>Небольшое изменение показывает, что <code>`...`</code> интерпретируется и внутри кавычек <code>"..."</code>:</p>
    <p><code>$ echo "At the tone</code></p>
    <p><code>&gt; the time will be `date`."</code></p>
    <p><code>At the tone</code></p>
    <p><code>the time will be Thu Sep 29 00:03:07 EDT 1983.</code></p>
    <p><code>$</code></p>
    <p>В качестве другого примера предположим, что вам необходимо послать почту группе людей, которые зарегистрированы под именем, хранящимся в файле <code>mailinglist</code>. Можно, конечно, отредактировать файл <code>mailinglist</code> так, чтобы он стал пригодным для применения команды <code>mail</code> и передать его интерпретатору, но значительно проще использовать команду</p>
    <p><code>$ mail `cat mailinglist` &lt;letter</code></p>
    <p>Запуск команды <code>cat</code> порождает список имен пользователей, и эти имена становятся аргументами команды <code>mail</code>. (При обработке результата выполнения команды, помещенной между знаками слабого ударения и используемой в качестве аргумента, интерпретатор считает символы перевода строки разделителями слов, а не символами завершения командной строки; подробнее данный вопрос обсуждается в гл. 5.) Работать со знаками слабого ударения нетрудно, и поэтому, действительно, нет нужды вводить отдельный флаг команды <code>mail</code>, задающий список адресатов.</p>
    <p>Несколько иной подход требуется для преобразования файла <code>mailinglist</code> из простого списка имен в программу, выдающую список имен:</p>
    <p><code>$ cat mailinglist</code></p>
    <p><code>echo don whr ejs mb </code>Новая версия</p>
    <p><code>$ cx mailinglist</code></p>
    <p><code>$ mailinglist</code></p>
    <p><code>don whr ejs mb</code></p>
    <p><code>$</code></p>
    <p>Теперь посылка писем адресатам из списка реализуется командой:</p>
    <p><code>$ mail `mailinglist` &lt;letter</code></p>
    <p>Добавив еще одну программу, получим возможность даже изменять список пользователей в диалоге. Такая программа называется <code>pick</code>:</p>
    <p><code>$ pick аргументы...</code></p>
    <p>и выдает свои аргументы по одному, ожидая каждый раз ответа. Результатом действия команды <code>pick</code> являются те аргументы, на которые был дан ответ <code>y</code> (<code>yes</code> — да); при всяком другом ответе аргумент отбрасывается. Например,</p>
    <p><code>$ pr `pick *.с` | lpr</code></p>
    <p>Здесь вначале выдаются имена файлов, оканчивающиеся на <code>.с</code>. Выбранные имена печатаются с помощью команд <code>pr</code> и <code>lpr</code>. (Команда <code>pick</code> не входит в состав команд седьмой версии, но она столь проста и полезна, что мы включили ее варианты в гл. 5 и 6).</p>
    <p>Допустим, вы используете второй вариант команды <code>mailinglist</code>. Тогда посылка писем адресатам <code>don</code> и <code>mb</code> выглядит так:</p>
    <p><code>$ mail `pick \`mailinglist\`` &lt;letter</code></p>
    <p><code>don? y</code></p>
    <p><code>whr?</code></p>
    <p><code>ejs?</code></p>
    <p><code>mb? y</code></p>
    <p><code>$</code></p>
    <p>Обратите внимание на вложенные знаки слабого ударения; обратная дробная черта запрещает обработку вложенной конструкции <code>`...`</code> при разборе внешних знаков слабого ударения.</p>
    <cite>
     <subtitle>Упражнение 3.10</subtitle>
     <p>Что произойдет, если опустить символы обратной дробной черты в команде</p>
     <p><code>$ echo `echo \`date\``</code></p>
    </cite>
    <cite>
     <subtitle>Упражнение 3.11</subtitle>
     <p>Попробуйте ввести</p>
     <p><code>$`date`</code></p>
     <p>и объясните результат.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 3.12</subtitle>
     <p>Команда</p>
     <p><code>$ grep -l pattern filenames</code></p>
     <p>перечисляет имена файлов, которые соответствуют шаблону, но больше ничего не выдает. Попытайтесь выполнить разные вариации такого задания:</p>
     <p><code>$ command `grep -l pattern filenames`</code></p>
    </cite>
   </section>
   <section>
    <title>
     <p>3.6 Переменные языка <code>shell</code></p>
    </title>
    <p>Подобно большинству языков программирования, <code>shell</code> имеет переменные, которые на программистском жаргоне называются <emphasis>параметрами</emphasis>. Такие строки, как <code>$1</code>, являются позиционными параметрами-переменными, хранящими аргументы командного файла. Цифра показывает положение параметра в командной строке. Ранее мы имели дело с другими переменными языка <code>shell</code>: <code>PATH</code> — список каталогов, в которых происходит поиск команд, <code>НОМЕ</code> — ваш начальный каталог и т.д. В отличие от переменных в обычном языке переменные, задающие позиционные параметры, не могут быть изменены; хотя <code>PATH</code> представляет собой переменную со значением <code>$PATH</code>, нет переменной 1 со значением <code>$1</code>, т.е. <code>$1</code> — это не что иное, как компактное обозначение первого аргумента.</p>
    <p>Если забыть о позиционных параметрах, переменные языка <code>shell</code> можно создавать, выбирать и изменять. Например,</p>
    <p><code>$ PATH=:/bin:/usr/bin</code></p>
    <p>означает присваивание, изменяющее список каталогов в процессе поиска. До и после знака равенства не должно быть пробелов. Присваиваемое значение должно выражаться одним словом, и его следует взять в кавычки, если оно содержит метасимволы, которые не нужно обрабатывать. Значение переменной выбирается, если предварить имя знаком доллара:</p>
    <p><code>$ PATH=$PATH:/usr/games</code></p>
    <p><code>$ echo $PATH</code></p>
    <p><code>:/usr/you/bin:/bin:/usr/bin:/usr/games</code></p>
    <p><code>$ PATH=:/usr/you/bin:/bin:/usr/bin </code>Восстановим значение</p>
    <p><code>$</code></p>
    <p>He все переменные имеют специальное значение для интерпретатора. Можно создавать новые переменные, присваивая им значения. По традиции переменные, имеющие специальное значение, обозначаются прописными буквами, а обычные переменные — строчными. Типичным примером использования переменных является хранение в них длинных строк, таких, как имена файлов:</p>
    <p><code>$ pwd</code></p>
    <p><code>/usr/you/bin</code></p>
    <p><code>$ dir=`pwd`        </code>Запомним, где находимся</p>
    <p><code>$ cd /usr/mary/bin </code>Перейдем в другое место</p>
    <p><code>$ ln $dir/cx .     </code>Используем переменную в имени файла</p>
    <p><code>$ ...              </code>Поработаем некоторое время</p>
    <p><code>$ cd $dir          </code>Вернемся</p>
    <p><code>$ pwd</code></p>
    <p><code>/usr/you/bin</code></p>
    <p><code>$</code></p>
    <p>Встроенная в интерпретатор команда <code>set</code> показывает значения всех определенных вами переменных. Для просмотра одной или двух переменных более подходит команда <code>echo</code>:</p>
    <p><code>$ set</code></p>
    <p><code>HOME=/usr/you</code></p>
    <p><code>IFS=</code></p>
    <empty-line/>
    <p><code>PATH=:/usr/you/bin:/bin/:/usr/bin</code></p>
    <p><code>PS1=$</code></p>
    <p><code>PS2=&gt;</code></p>
    <p><code>dir=/usr/you/bin</code></p>
    <p><code>$ echo $dir</code></p>
    <p><code>/usr/you/bin</code></p>
    <p><code>$</code></p>
    <p>Значение переменной связано с той копией интерпретатора, который создал ее, и автоматически не передается процессам — потомкам интерпретатора.</p>
    <p><code>$ x=Hello </code>Создание x</p>
    <p><code>$ sh      </code>Новый shell</p>
    <p><code>$ echo $x </code>Происходит только перевод строки,</p>
    <p> x не определено в порожденном интерпретаторе</p>
    <p><code>$ ctl-d   </code>Возврат в исходный интерпретатор</p>
    <p><code>$ echo $x</code></p>
    <p><code>Hello     </code>x по-прежнему определено</p>
    <p><code>$</code></p>
    <p>Это означает, что в командном файле нельзя изменить значение переменной, поскольку выполнением командного файла управляет порожденный интерпретатор:</p>
    <p><code>$ echo 'x="Good bye" </code>Создание shell-файла из двух строк…</p>
    <p><code>&gt; echo $x' &gt;setx     </code>…для определения и печати x</p>
    <p><code>$ cat setx</code></p>
    <p><code>x="Good Bye"</code></p>
    <p><code>echo $x</code></p>
    <p><code>$ echo $x</code></p>
    <p><code>Hello                </code>x есть Hello в исходном интерпретаторе</p>
    <p><code>$ sh setx</code></p>
    <p><code>Good Bye             </code>x есть Good Bye в порожденном интерпретаторе…</p>
    <p><code>$ echo $x</code></p>
    <p><code>Hello                </code>…но по-прежнему есть Hello в текущем интерпретаторе</p>
    <p><code>$</code></p>
    <p>Однако бывают ситуации, когда было бы полезно изменять переменные интерпретатора в командном файле. Очевидным примером является файл, добавляющий новый каталог к вашей переменной <code>PATH</code>. Поэтому интерпретатор предоставляет команду <code>'.'</code> (точка), которая выполняет команды из файла в текущем, а не порожденном интерпретаторе. Первоначально это было задумано для удобства пользователей, чтобы они могли повторно выполнять свой файл <code>.profile</code>, не входя заново в систему, но в принципе это открывает и другие возможности:</p>
    <p><code>$ cat /usr/you/bin/games</code></p>
    <p><code>PATH=$PATH:/usr/games </code>Добавим /usr/games к PATH</p>
    <p><code>$ echo $PATH</code></p>
    <p><code>:/usr/you/bin:/bin:/usr/bin</code></p>
    <p><code>$ . games</code></p>
    <p><code>$ echo $PATH</code></p>
    <p><code>:/usr/you/bin:/bin:/usr/bin:/usr/games</code></p>
    <p><code>$</code></p>
    <p>Поиск файла для команды <code>'.'</code> осуществляется с помощью переменной <code>PATH</code>, так что его можно поместить в ваш каталог <code>bin</code>.</p>
    <p>Когда используется команда <code>'.'</code>, только условно можно считать, что выполняется командный файл. Файл не "выполняется" в обычном смысле этого слова. Команды из него запускаются, как если бы вы ввели их в диалоговом режиме: стандартный входной поток интерпретатора временно переключается на файл. Поскольку файл читается, не нужно иметь право на его выполнение. Другое отличие состоит в том, что файл не получает аргументов командной строки; <code>$1</code>, <code>$2</code> и т.д. являются пустыми строками. Было бы неплохо, если бы аргументы передавались, но этого не происходит.</p>
    <p>Есть еще один способ установить значение переменной в порожденном интерпретаторе — присвоить его явно в командной строке перед самой командой:</p>
    <p><code>$ echo 'echo $x' &gt;echox</code></p>
    <p><code>$ cx echox</code></p>
    <p><code>$ echo $x</code></p>
    <p><code>Hello </code>Как и прежде</p>
    <p>x не определено в порожденном интерпретаторе</p>
    <p><code>$ x=Hi echox</code></p>
    <p><code>Hi    </code>Значение x передается порожденному интерпретатору</p>
    <p><code>$</code></p>
    <p>(Первоначально присваивания всюду в командной строке передавались команде, но это противоречило <code>dd(1)</code>.)</p>
    <p>Операцию <code>'.'</code> следует использовать, чтобы навсегда изменить значение переменной, тогда как присваивания в командной строке предназначены для временных изменений. В качестве примера рассмотрим еще раз поиск команд в каталоге <code>/usr/games</code>, не указанном в вашей переменной <code>PATH</code>:</p>
    <p><code>$ ls /usr/games | grep fort</code></p>
    <p><code>fortune</code> Игровая команда fortune</p>
    <p><code>$ fortune</code></p>
    <p><code>fortune: not found</code></p>
    <p><code>$ echo $PATH</code></p>
    <p><code>:/usr/you/bin:/bin:/usr/bin</code> /usr/games не входит в PATH</p>
    <p><code>$ PATH=/usr/games fortune</code></p>
    <p><code>Позвони в звонок; закрой книгу; задуй свечу.</code></p>
    <p><code>$ echo $PATH</code></p>
    <p><code>:/usr/you/bin:/bin:/usr/bin </code>PATH не изменилось.</p>
    <p><code>$ cat /usr/you/bin/games    </code>команда games все еще здесь</p>
    <p><code>$ . games</code></p>
    <p><code>$ fortune</code></p>
    <p><code>Непродуманная оптимизация - источник всех бед - Кнут</code></p>
    <p><code>$ echo $PATH</code></p>
    <p><code>:/usr/you/bin:/bin:/usr/bin:/usr/games</code> Сейчас PATH изменилось</p>
    <p><code>$</code></p>
    <p>Можно использовать оба средства в одном командном файле. Вы можете несколько видоизменить команду <code>games</code> для запуска одной игровой программы без изменения переменной <code>PATH</code> или постоянно переопределять <code>PATH</code>, чтобы она включала <code>/usr/games</code>:</p>
    <p><code>$ cat /usr/you/bin/games</code></p>
    <p><code>PATH=$PATH:/usr/games $*    </code>Обратите внимание на $*</p>
    <p><code>$ cx /usr/you/bin/games</code></p>
    <p><code>$ echo $PATH</code></p>
    <p><code>:/usr/you/bin:/bin:/usr/bin </code>/usr/games не входит</p>
    <p><code>$ games fortune</code></p>
    <p><code>Готов отдать свою правую руку, чтобы одинаково владеть обеими</code></p>
    <p><code>$ echo $PATH</code></p>
    <p><code>:/usr/you/bin:/bin:/usr/bin </code>Все еще не входит</p>
    <p><code>$ . games</code></p>
    <p><code>$ echo $PATH</code></p>
    <p><code>:/usr/you/bin:/bin:/usr/bin:/usr/games</code> Теперь входит</p>
    <p><code>$ fortune</code></p>
    <p><code>Тот, кто медлит, иногда спасается</code></p>
    <p><code>$</code></p>
    <p>При первом обращении к games командный файл выполняется в порожденном интерпретаторе, в котором переменная <code>PATH</code> временно изменена так, чтобы включать каталог <code>/usr/games</code>. В то же время во втором примере файл обрабатывается текущим интерпретатором при значении <code>$*</code>, являющемся пустой строкой, поэтому в строке нет команд и переменная <code>PATH</code> изменяется. Применение команды <code>games</code> в обоих случаях достаточно нетривиально, но в результате получаем удобное и естественное для использования средство.</p>
    <p>Для того чтобы значение переменной было доступно в порожденном интерпретаторе, следует использовать команду <code>export</code> языка <code>shell</code>. (Вы можете поразмышлять о том, почему нет возможности экспортировать значение переменной от порожденного интерпретатора к порождающему его.) Приведем один из рассмотренных выше примеров, но теперь с экспортом переменной:</p>
    <p><code>$ x=Hello</code></p>
    <p><code>$ export x</code></p>
    <p><code>$ sh           </code>Новый интерпретатор</p>
    <p><code>$ echo $x</code></p>
    <p><code>Hello          </code>x доступно в порожденном интерпретаторе</p>
    <p><code>$ x='Good Bye' </code>Изменим значение x</p>
    <p><code>$ echo $x</code></p>
    <p><code>Good Bye</code></p>
    <p><code>$ ctl-d        </code>Выйдем из порожденного интерпретатора</p>
    <p><code>$              </code>Снова в исходном интерпретаторе</p>
    <p><code>$ echo $x</code></p>
    <p><code>Hello          </code>x по-прежнему Hello</p>
    <p><code>$</code></p>
    <p>Семантика команды <code>export</code> нетривиальна, но по крайней мере для повседневных нужд достаточно придерживаться основного правила: никогда не экспортируйте временные переменные, служащие для краткосрочных целей, и всегда экспортируйте переменные, необходимые вам во всех порожденных интерпретаторах (включая, например, интерпретаторы, запускаемые командой <code>!</code> редактора <code>ed</code>). Поэтому переменные, имеющие специальное значение для интерпретатора, такие, как <code>PATH</code> и <code>НОМЕ</code>, следует экспортировать.</p>
    <cite>
     <subtitle>Упражнение 3.13</subtitle>
     <p>Почему в значение переменной <code>PATH</code> всегда включается текущий каталог? Куда его нужно поместить?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>3.7 Еще раз о переключении ввода-вывода</p>
    </title>
    <p>Понятие стандартного потока диагностики было введено для того, чтобы сообщения об ошибках всегда появлялись на терминале:</p>
    <p><code>$ diff file1 file2 &gt;diff.out</code></p>
    <p><code>diff: file2: No such file or directory</code></p>
    <p><code>$</code></p>
    <p>Без сомнения, сообщения об ошибке должны появляться подобным образом — было бы крайне неприятно, если бы они исчезли в файле <code>diff.out</code>, оставляя вас в уверенности, что ошибочная команда <code>diff</code> выполнена правильно.</p>
    <p>В начале выполнения каждой программы определены по умолчанию три файла, обозначаемые небольшими целыми числами и называемые <emphasis>дескрипторами файла</emphasis> (мы рассмотрим их в гл. 7). Со стандартными входным (0) и выходным (1) потоками вы уже знакомы: они часто переключаются на файл или программный канал. Последний поток с номером 2 представляет собой стандартный поток диагностики и обычно предназначается для вывода на терминал.</p>
    <p>Иногда программы осуществляют вывод в стандартный поток диагностики, даже если они работают правильно. Типичным примером является программа time, которая выполняет команду и выдает в стандартный поток диагностики сообщение о том, сколько времени заняло выполнение:</p>
    <p><code>$ time wc ch3.1</code></p>
    <p><code>931 4288 22691 ch3.1</code></p>
    <empty-line/>
    <p><code>real 1.0</code></p>
    <p><code>user 0.4</code></p>
    <p><code>sys  0.4</code></p>
    <p><code>$ time wc ch3.1 &gt;wc.out</code></p>
    <empty-line/>
    <p><code>real 2.0</code></p>
    <p><code>user 0.4</code></p>
    <p><code>sys  0.3</code></p>
    <p><code>$ time wc ch3.1 &gt;wc.out 2&gt;time.out</code></p>
    <p><code>$ cat time.out</code></p>
    <empty-line/>
    <p><code>real 1.0</code></p>
    <p><code>user 0.4</code></p>
    <p><code>sys  0.3</code></p>
    <p><code>$</code></p>
    <p>Конструкция <code>2&gt; имя_файла</code> (между <code>2</code> и <code>&gt;</code> не должно быть пробелов) переключает стандартный поток диагностики на файл; синтаксически она непривлекательна, но служит своей цели. (Для такого короткого теста, как приведенный выше, время, выдаваемое командой <code>time</code>, не совсем правильное, но для последовательности больших тестов она выводит полезную информацию, которой можно доверять в разумных границах. Вы вполне можете сохранить ее для дальнейшего анализа; обратитесь, например, к таблице 8.1.)</p>
    <p>Допустимо также слияние двух выходных потоков:</p>
    <p><code>$ time wc ch3.1 &gt;wc.out 2&gt;&amp;1</code></p>
    <p><code>$ cat wc.out</code></p>
    <p><code>931 4288 22691 ch3.1</code></p>
    <empty-line/>
    <p><code>real 1.0</code></p>
    <p><code>user 0.4</code></p>
    <p><code>sys  0.3</code></p>
    <p><code>$</code></p>
    <p>Обозначение <code>2&gt;&amp;1</code> является указанием интерпретатору, что стандартный поток диагностики нужно поместить в тот же поток, что и стандартный выходной. Амперсанд не содержит какого-либо мнемонического смысла; это просто идиома, которую следует запомнить. Для добавления стандартного выходного потока к стандартному потоку диагностики можно использовать <code>1&gt;&amp;2</code>:</p>
    <p><code>echo ... 1&gt;&amp;2</code></p>
    <p>В командных файлах это позволяет предотвратить исчезновение сообщений в файле или программном канале.</p>
    <p>Интерпретатор предоставляет возможность размещать стандартный входной поток вместе с командой, а не в отдельном файле, так что командный файл может хранить всю информацию в себе самом. Наша справочная программа <code>411</code>, работающая с каталогом телефонов, могла быть задана так:</p>
    <p><code>$ cat 411</code></p>
    <p><code>grep "$*" &lt;&lt;End</code></p>
    <p><code>dial-a-joke      212-976-3838</code></p>
    <p><code>dial-a-prayer    212-246-4200</code></p>
    <p><code>dial santa       212-976-3636</code></p>
    <p><code>dow jones report 212-976-4141</code></p>
    <p><code>End</code></p>
    <p><code>$</code></p>
    <p>Программирующие на языке <code>shell</code> называют такую конструкцию "документ здесь", т.е. входной поток находится здесь, а не в каком-нибудь файле. Началом конструкции служит <code>&lt;&lt;</code>; последующее слово (в нашем примере <code>End</code>) является ограничителем входного потока, включающего все строки до той, которая содержит только данное слово. Интерпретатор выполняет замену конструкций <code>$</code>, <code>`...`</code> и <code>\</code> в "документе здесь", если только часть слова не экранирована кавычками или обратной дробной чертой, — в этом случае весь документ берется без изменений. В конце главы мы рассмотрим еще более интересный пример с конструкцией "документ здесь".</p>
    <p>В табл. 3.2 перечислены различные виды переключения ввода-вывода, допускаемые интерпретатором.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>&gt; файл</code></td>
      <td align="left" valign="top">Переключение стандартного выходного потока в файл</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>&gt;&gt; файл</code></td>
      <td align="left" valign="top">Добавление стандартного выходного потока в файл</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>&lt; файл</code></td>
      <td align="left" valign="top">Получение стандартного выходного потока из файла</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>p1 | p2</code></td>
      <td align="left" valign="top">Передача стандартного выходного потока программы <code>p1</code> в качестве входного потока для программы <code>p2</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>^</code></td>
      <td align="left" valign="top">Устарелый синоним <code>|</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>n&gt; файл</code></td>
      <td align="left" valign="top">Переключение выходного потока из файла с дескриптором n в файл</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>n&gt;&gt; файл</code></td>
      <td align="left" valign="top">Добавление выходного потока из файла с дескриптором n в файл</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>n&gt;&amp;m</code></td>
      <td align="left" valign="top">Слияние выходных потоков файлов с дескрипторами <code>n</code> и <code>m</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>&lt;&lt;s</code></td>
      <td align="left" valign="top">"Документ здесь": берется стандартный входной поток до строки, начинающейся с <code>s</code>; выполняется подстановка для <code>$</code>, <code>`...`</code> и <code>\</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>&lt;&lt;\s</code></td>
      <td align="left" valign="top">"Документ здесь" без подстановки</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>&lt;&lt;'s'</code></td>
      <td align="left" valign="top">"Документ здесь" без подстановки</td>
     </tr>
    </table>
    <p>Таблица 3.2: <strong>Переключение ввода-вывода интерпретатора</strong></p>
    <empty-line/>
    <cite>
     <subtitle>Упражнение 3.14</subtitle>
     <p>Сравните версии программы 411: использующую "документ здесь" и первоначальную. Какую легче сопровождать? Какая более подходит в качестве основы общего служебного средства?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>3.8 Циклы в shell-программах</p>
    </title>
    <p>Язык <code>shell</code> — действительно язык программирования: в нем есть переменные, циклы, ветвления и т.п. Здесь мы обсудим основные циклы, а структуры управления рассмотрим более подробно в гл. 5.</p>
    <p>Типичным считается цикл по последовательности имен файлов, и оператор <code>for</code> языка <code>shell</code> является единственной структурой управления, которую обычно задают с терминала, а не помещают в файл для последующего выполнения. Синтаксис оператора <code>for</code> таков:</p>
    <p><code>for перем in список_слов</code></p>
    <p><code>do</code></p>
    <p><code> команды</code></p>
    <p><code>done</code></p>
    <p>Например, для получения эха имен файлов по одному на строке достаточно задать:</p>
    <p><code>$ for i in *</code></p>
    <p><code>&gt; do</code></p>
    <p><code>&gt;  echo $i</code></p>
    <p><code>&gt; done</code></p>
    <p>Вместо <code>i</code> можно применять любую переменную языка <code>shell</code>, но это обозначение традиционно. Заметьте, что значение переменной получается с помощью <code>$i</code>, однако в заголовке цикла переменную указывают как <code>i</code>. Мы задействовали <code>*</code> для выбора всех файлов текущего каталога, но можно использовать и любой другой список аргументов. Обычно нужно сделать что-нибудь более интересное, чем печать имен файлов. Нам часто приходилось сравнивать набор файлов с их предыдущими версиями, например старую версию гл. 2 (хранимую в каталоге <code>old</code>) с текущей:</p>
    <p><code>$ ls ch2. * | 5</code></p>
    <p><code>ch2.1 ch2.2 ch2.3 ch2.4 ch2.5</code></p>
    <p><code>ch2.6 ch2.7</code></p>
    <p><code>$ for i in ch2.*</code></p>
    <p><code>&gt; do</code></p>
    <p><code>&gt;  echo $i</code></p>
    <p><code>&gt;  diff -b old/$i $i</code></p>
    <p><code>&gt; echo </code>Добавим пустую строку для красоты</p>
    <p><code>&gt; done | pr -h "diff `pwd`/old `pwd` | lpr &amp;</code></p>
    <p><code>3712   </code>Номер процесса</p>
    <p><code>$</code></p>
    <p>Выходной поток направлен по конвейеру через команды <code>pr</code> и <code>lpr</code> просто для того, чтобы показать, что это возможно: стандартный выходной поток программ, находящихся внутри цикла <code>for</code>, попадает в стандартный выходной поток самой команды <code>for</code>. С помощью флага <code>-h</code> в команде pr мы поместили в выходной поток заголовок с "архитектурными излишествами", используя два вложенных обращения к <code>pwd</code>. Вся последовательность команд запущена асинхронно (<code>&amp;</code>), так что не нужно ждать ее окончания; <code>&amp;</code> применяется ко всякому циклу и конвейеру.</p>
    <p>Мы предпочитаем указанный формат для цикла <code>for</code>, но вы можете сократить его. Единственное ограничение заключается в том, что <code>do</code> и <code>done</code> распознаются как ключевые слова, только если они появляются сразу после перевода строки или точки с запятой. В зависимости от размера цикла <code>for</code> иногда лучше помещать все на одной строке:</p>
    <p><code>for i in список; do команды; done</code></p>
    <p>Следует использовать цикл <code>for</code> для обработки составных команд или в тех случаях, когда не подходит встроенная обработка отдельных команд. Но не применяйте его там, где в отдельной команде есть цикл по именам файлов:</p>
    <p><code># Плохая идея:</code></p>
    <p><code>for i in $*</code></p>
    <p><code>do</code></p>
    <p><code> chmod +x $i</code></p>
    <p><code>done</code></p>
    <p>Предпочтительнее сделать так:</p>
    <p><code>chmod +x $*</code></p>
    <p>поскольку в цикле <code>for</code> отдельная команда <code>chmod</code> выполняется для каждого файла, что требует больших вычислительных ресурсов. (Убедитесь в том, что вы понимаете разницу между командами</p>
    <p><code>for i in *</code></p>
    <p>в которой цикл выполняется по всем именам файлов текущего каталога, и</p>
    <p><code>for i in $*</code></p>
    <p>в которой цикл выполняется по всем аргументам командного файла.)</p>
    <p>Список аргументов для цикла <code>for</code> часто получают путем выбора имен файлов по шаблону, но можно получать и любым другим способом, в частности:</p>
    <p><code>for i in `cat ...`</code></p>
    <p>или просто вводом аргументов. Например, ранее в этой главе мы создали ряд программ для печати в несколько столбцов под именами <code>2</code>, <code>3</code> и т.д. Они являются связями с одним файлом, которые можно установить следующим образом (при условии, что программа <code>2</code> написана):</p>
    <p><code>$ for i in 3 4 5 6; do ln 2 $i; done</code></p>
    <p><code>$</code></p>
    <p>Цикл <code>for</code> имеет и более интересное назначение. Выберем с помощью команды <code>pick</code> те файлы, которые будут сравниваться с файлами из каталога старых версий:</p>
    <p><code>$ for i in `pick ch2.*`</code></p>
    <p><code>&gt; do</code></p>
    <p><code>&gt;  echo $i:</code></p>
    <p><code>&gt;  diff old/$i $i</code></p>
    <p><code>&gt; done | pr | lpr</code></p>
    <p><code>ch2.1? y</code></p>
    <p><code>ch2.2</code></p>
    <p><code>ch2.3</code></p>
    <p><code>ch2.4? y</code></p>
    <p><code>ch2.5? y</code></p>
    <p><code>ch2.6?</code></p>
    <p><code>ch2.7?</code></p>
    <p><code>$</code></p>
    <p>Очевидно, данный цикл следует поместить в командный файл, чтобы уменьшить ввод в следующий раз (ведь если вы что-то сделали дважды, вероятно, вы сделаете это и в третий раз).</p>
    <cite>
     <subtitle>Упражнение 3.15</subtitle>
     <p>Если цикл с командой <code>diff</code> хранится в командном файле, поместите ли вы туда команду pick? Объясните, почему.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 3.16</subtitle>
     <p>Что произойдет, если последняя строка приведенного цикла будет иметь вид:</p>
     <p><code>&gt; done | pr | lpr &amp;</code></p>
     <p>т.е. кончаться амперсандом? Попробуйте сделать прогноз, а затем проверьте его.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>3.9 Программа <code>bundle</code>: соберем все воедино</p>
    </title>
    <p>Чтобы лучше понять, как создаются командные файлы, обратимся к такому примеру. Предположим, вы получили почту от приятеля с другой машины: <code>"где-то!боб"</code> (Существует несколько вариантов обозначений для адресата на другой машине. Наиболее общим является следующее: <emphasis>машина!пользователь</emphasis><a l:href="#n10" type="note">[10]</a>. См. справочное руководство по <code>mail(1)</code>), и он хотел бы скопировать командные файлы из вашего каталога <code>bin</code>. Самый простой способ их пересылки заключается в ответной почте, так что вы могли бы начать вводить:</p>
    <p><code>$ cd /usr/you/bin</code></p>
    <p><code>$ for i in `pick *`</code></p>
    <p><code>&gt; do</code></p>
    <p><code>&gt;  echo ============== Это файл $i ==============</code></p>
    <p><code>&gt;  cat $i</code></p>
    <p><code>&gt; done | mail где-то!боб</code></p>
    <p><code>$</code></p>
    <p>Однако посмотрим на это с точки зрения адресата <code>"где-то!боб"</code>: он должен получить почту, в которой все файлы четко разделены, но ему придется воспользоваться редактором для разбивки сообщений на отдельные файлы. Для того чтобы адресату ничего не надо было делать, почтовое сообщение, построенное подходящим образом, должно автоматически распаковать себя, а значит, оно должно быть командным файлом, содержащим и сами файлы, и операции по их распаковке. Вторая идея заключается в том, что конструкция языка <code>shell</code> "документ здесь" является удобным способом задания информации для команды при ее запуске. Тогда остальная часть задачи сводится к тому, чтобы правильно расставить кавычки. Ниже приведена работающая программа bundle, которая группирует файлы в выходной поток самодокументированного командного файла:</p>
    <p><code>$ cat bundle</code></p>
    <p><code># bundle: группирует файлы в распределенный пакет</code></p>
    <empty-line/>
    <p><code>echo '# Для разбиения на файлы вызовите sh с этим файлом'</code></p>
    <p><code>for i</code></p>
    <p><code>do</code></p>
    <p><code> echo "echo $i 1&gt;&amp;2"</code></p>
    <p><code> echo "cat &gt;$i &lt;&lt;'End of $i'"</code></p>
    <p><code> cat $i</code></p>
    <p><code> echo "End of $i"</code></p>
    <p><code>done</code></p>
    <p><code>$</code></p>
    <p>Поскольку мы взяли в кавычки <code>"End of $i"</code>, любые метасимволы из файлов будут игнорироваться.</p>
    <p>Естественно, что вам следует выполнить пробный запуск программы, чтобы не нанести ущерб адресату <code>"где-то!боб"</code>:</p>
    <p><code>$ bundle cx lc &gt;junk                </code>Пробный запуск bundle</p>
    <p><code>$ cat junk</code></p>
    <p><code># Для разбиения на файлы вызовите sh с этим файлом</code></p>
    <p><code>echo cx 1&gt;&amp;2</code></p>
    <p><code>cat &gt;cx &lt;&lt;'End of cx'</code></p>
    <p><code>chmod +x сх</code></p>
    <p><code>End of cx</code></p>
    <p><code>echo lc 1&gt;&amp;2</code></p>
    <p><code>cat &gt;lc &lt;&lt;'End of lc'</code></p>
    <p><code># lc: подсчет числа строк в файлах</code></p>
    <p><code>wc -l $*</code></p>
    <p><code>End of lc</code></p>
    <p><code>$ mkdir test</code></p>
    <p><code>$ sh ../junk                        </code>Попробуем</p>
    <p><code>cx</code></p>
    <p><code>lc</code></p>
    <p><code>$ ls</code></p>
    <p><code>cx</code></p>
    <p><code>lc</code></p>
    <p><code>$ cat cx</code></p>
    <p><code>chmod +x $*</code></p>
    <p><code>$ cat lc</code></p>
    <p><code># lc: подсчет числа строк в файлах</code></p>
    <p><code>wc -l $*</code> Похоже верно</p>
    <p><code>$ cd ..</code></p>
    <p><code>$ rm junk test/*; rmdir test        </code>Удалим ненужное</p>
    <p><code>$ pwd</code></p>
    <p><code>/usr/you/bin</code></p>
    <p><code>$ bundle `pick *` | mail где-то!боб </code>Посылка файлов</p>
    <p><code>$</code></p>
    <p>Здесь могут возникнуть трудности, если окажется, что один из посылаемых файлов содержит строку вида</p>
    <p><code>End of имя_файла</code></p>
    <p>но это маловероятное событие. Для обеспечения полной надежности программы нам потребуются некоторые из описываемых в последующих главах средства, однако и в таком виде она удивительно полезна и удобна.</p>
    <p>Программа <code>bundle</code> является хорошим примером приспособляемости программного мира UNIX: в ней используются циклы языка <code>shell</code>, переключение ввода-вывода, конструкция "документ здесь" и командные файлы. Она непосредственно обращается к команде <code>mail</code>, и, что особенно интересно, порождает программу. Это одна из самых "красивых" среди известных вам <code>shell</code>-программ: файл в несколько строк предлагает простое и элегантное решение.</p>
    <cite>
     <subtitle>Упражнение 3.17</subtitle>
     <p>Как бы вы использовали <code>bundle</code> для посылки всех файлов с учетом вложенных каталогов? <emphasis>Подсказка</emphasis>: командные файлы могут быть рекурсивными.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 3.18</subtitle>
     <p>Модифицируйте программу <code>bundle</code> так, чтобы к каждому файлу она добавляла информацию, выведенную командой <code>ls -l</code>, в частности права доступа и время его последнего изменения. Сравните возможности <code>bundle</code> и архивной программы <code>ar(1)</code>.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>3.10 Для чего нужно программировать на языке shell!</p>
    </title>
    <p>Программа <code>shell</code> системы UNIX не относится к типичным интерпретаторам команд, хотя и дает возможность запускать команды обычным способом. Тем не менее это язык программирования, который позволяет достичь большего. Имеет смысл сделать ретроспективный обзор данной главы, поскольку здесь приведен довольно обширный материал, и, кроме того (что является главной причиной), мы обещали вам обсудить "средства общего пользования", а затем увлеклись примерами программирования на языке <code>shell</code>. Дело в том, что используя язык <code>shell</code>, вы все время пишите маленькие, практически однострочные программы, в частности конвейер — это программа, равноценная фразе "Чай готов". Однако вы выполняете свою работу так легко и естественно (если умеете), что даже не считаете ее программированием.</p>
    <p>Интерпретатор дает вам такие средства, как циклы, переключение ввода-вывода с помощью <code>&lt;</code> и <code>&gt;</code>, порождение имен файлов с помощью <code>*</code>, причем применение этих средств единообразно во всех программах. Некоторые средства, например командные файлы и программные каналы, на самом деле обеспечиваются ядром, но язык <code>shell</code> предоставляет естественную запись для их создания. Они не только удобны, но и увеличивают мощность системы в целом.</p>
    <p>Базой для интерпретатора служит ядро системы UNIX, например, хотя интерпретатор и определяет конвейеры, именно ядро осуществляет передачу данных по ним. Способ, которым система обрабатывает выполняемые файлы, позволяет программировать командные файлы так, чтобы они выполнялись подобно оттранслированным программам. Пользователь не должен думать о том, что это командные файлы, — для передачи их на выполнение не требуется специальная команда типа <code>RUN</code>. Сам интерпретатор является программой, а не частью ядра. Его можно настраивать, расширять и обращаться с ним, как с любой другой программой. Такой подход не является уникальным, но здесь он реализован полнее, чем где бы то ни было.</p>
    <p>В гл. 5 мы вернемся к теме программирования на языке <code>shell</code>, а пока запомните: вне зависимости от того, как вы работаете с интерпретатором, вы программируете на его языке (чем в основном и объясняются его достоинства).</p>
    <cite>
     <subtitle>Историческая и библиографическая справка</subtitle>
     <p>На языке интерпретатора программируют с незапамятных времен. Сперва были отдельные команды для <code>if</code>, <code>goto</code> и меток, а команда <code>goto</code> выполнялась путем просмотра входного файла от его начала до нужной метки. (Поскольку невозможно читать заново через программный канал, нельзя было и передавать по программному каналу в командный файл, использующий структуры управления.)</p>
     <p>Седьмая версия интерпретатора была создана С. Боурном, которому оказал помощь и идейную поддержку Д. Мэшей. Как вы увидите в гл. 5, здесь есть все необходимое для программирования. Кроме того, реорганизована работа с входным и выходным потоками: теперь можно без ограничения переключать ввод-вывод из командных файлов и в них. Неотъемлемым свойством интерпретатора является обработка метасимволов в именах файлов; в ранних версиях, которые остались лишь на очень маленьких машинах, она реализовывалась отдельной программой.</p>
     <p>Другой вариант интерпретатора, с которым вы могли встречаться (а может быть, вы предпочитаете с ним работать) — <code>csh</code>, так называемый Си-<code>shell</code>, созданный Б. Джоем на базе интерпретатора шестой версии. По сравнению с интерпретатором Боурна этот интерпретатор лучше обеспечивает диалог. Он предоставляет средство "история", позволяющее повторять в сокращенной записи (возможно, с небольшим редактированием) предварительно введенные команды. Отличается также и синтаксис команд интерпретатора Джоя. Но, поскольку Си-<code>shell</code>, базируется на интерпретаторе ранней версии, в нем содержится меньше средств для программирования; это скорее диалоговый интерпретатор команд, чем язык программирования. В частности, исключена передача по программному каналу из (или в) командного файла со структурами управления.</p>
     <p>Команда <code>pick</code> предложена Т. Даффом, а команда <code>bundle</code> — независимо А. Хьитом и Д. Гослингом.</p>
    </cite>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 4</p>
    <p>Фильтры</p>
   </title>
   <section>
    <p>Существует большое число программ UNIX, которые читают входной поток, выполняют простые операции над ним и записывают результат в выходной поток. Примерами могут служить программы <code>grep</code> и <code>tail</code>, выбирающие часть входного потока, <code>sort</code>, сортирующая его, <code>wc</code>, производящая подсчет в нем, и т.д. Такие программы называются <emphasis>фильтрами</emphasis>.</p>
    <p>В настоящей главе обсуждаются наиболее часто используемые фильтры. Первой мы рассмотрим программу <code>grep</code>, сосредоточившись на более сложных шаблонах, чем описанные в гл. 1, а затем две другие родственные программы — <code>egrep</code> и <code>fgrep</code>. Далее вы познакомитесь с еще несколькими полезными фильтрами, включая tr, который предназначен для транслитерации символов, <code>dd</code>, предназначенный для работы с данными, полученными из других систем, и <code>uniq</code> — для обнаружения повторяющихся строк. Приводится дополнительная информация и о программе <code>sort</code>.</p>
    <p>Конец главы посвящен двум преобразователям данных общего назначения, или программируемым фильтрам. Они называются так потому, что конкретное преобразование записывается как программа на некотором простом языке программирования. Различные программы могут породить совершенно разные преобразования. Речь идет здесь о программах <code>sed</code> ("stream editor" — потоковый редактор) и <code>awk</code>, имя которой составлено из начальных букв имен ее авторов. Обе программы получаются путем обобщения команды <code>grep</code>:</p>
    <p><code>$ программа шаблон-действие имена_файлов...</code></p>
    <p>которая сканирует последовательность файлов, ведя поиск строк, совпадающих с шаблоном, — если строка найдена, выполняется необходимое действие. Для команды <code>grep</code>, как и для редактора <code>ed</code>, шаблоном является регулярное выражение, а действие по умолчанию сводится к печати каждой строки, соответствующей шаблону.</p>
    <p>В программах <code>sed</code> и <code>awk</code> обобщаются и шаблоны, и действия. Команда <code>sed</code>, производная от <code>ed</code>, выполняет "программу", состоящую из команд редактирования. Она пропускает данные из файлов через эту программу, выполняя для каждой строки команды из программы. Команда <code>awk</code> не так удобна, как <code>sed,</code> для манипуляций с текстом, но в ней предусмотрены арифметические операции, переменные, встроенные функции и язык программирования, схожий с Си. В данной главе не приводится полное описание обеих программ; оно есть в т. 2B справочного руководства по UNIX.</p>
   </section>
   <section>
    <title>
     <p>4.1 Семейство программ <code>grep</code></p>
    </title>
    <p>В гл. 1 мы кратко упомянули о команде <code>grep</code>, а затем использовали ее в примерах. Конструкция</p>
    <p><code>$ grep шаблон имена_файлов</code></p>
    <p>проводит поиск в поименованных файлах или в стандартном входном потоке и выводит на печать каждую строку, в которую входит шаблон. Команда <code>grep</code> неоценима для поиска переменных в программах и слов в документах, а также для выбора части выходного потока программы:</p>
    <p><code>$ grep -n variable *.[гл]           </code>Поиск variable в тексте на Си</p>
    <p><code>$ grep From $MAIL                   </code>Печать заголовков сообщений из почтовой</p>
    <p>посылки</p>
    <p><code>$ grep From $MAIL | grep -v mary    </code>Заголовки, которые получены не от</p>
    <p>адресата mary</p>
    <p><code>$ grep -y mary $HOME/lib/phone-book </code>Поиск номера mary</p>
    <p><code>$ who | grep mary                   </code>Выяснить, работает ли mary в системе</p>
    <p><code>$ ls | grep -v temp                 </code>Имена файлов, не содержащих temp</p>
    <p>Флаг <code>-n</code> инициирует вывод номеров строк, флаг <code>-v</code> меняет на противоположное значение условия, а флаг <code>-y</code> допускает сопоставление строчных букв из шаблона с прописными буквами из файла (но прописные буквы все-таки могут сопоставляться только с прописными).</p>
    <p>Во всех рассматривавшихся до сих пор примерах проводился поиск обычных строк из букв и чисел. Но команда <code>grep</code> может искать и более сложные шаблоны: она интерпретирует выражения согласно простому языку для описания строк. С технической точки зрения шаблон представляет в некоторой степени ограниченную форму спецификаций строк, называемую регулярным выражением. Команда интерпретирует такие же регулярные выражения, как и редактор <code>ed</code>. На самом деле, эта команда была создана (за один вечер) прямым редактированием <code>ed</code>.</p>
    <p>Регулярные выражения характеризуются тем, что ряду символов, таким, как <code>*</code> и т.п., приписывается специальное значение, используемое интерпретатором. Есть еще несколько метасимволов, но, к сожалению, с различными значениями. В табл. 4.1 показаны все метасимволы регулярных выражений, и мы кратко их здесь рассмотрим.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>с</code></td>
      <td align="left" valign="top">Любой неспециальный символ c соответствует самому себе</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\c</code></td>
      <td align="left" valign="top">Указание убрать любое специальное значение символа <code>c</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>^</code></td>
      <td align="left" valign="top">Начало строки</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$</code></td>
      <td align="left" valign="top">Конец строки</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.</code></td>
      <td align="left" valign="top">Любой одиночный символ</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>[...]</code></td>
      <td align="left" valign="top">Любой символ из <code>...;</code> допустимы диапазоны типа <code>a-z</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>[^...]</code></td>
      <td align="left" valign="top">Любой символ не из <code>...</code>; допустимы диапазоны</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\n</code></td>
      <td align="left" valign="top">Строка, соответствующая n-му выражению <code>\(...\)</code> (только для <code>grep</code>)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>r*</code></td>
      <td align="left" valign="top">Нуль или более вхождений <code>r</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>r+</code></td>
      <td align="left" valign="top">Одно или более вхождений <code>r</code> (только для egrep)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>r?</code></td>
      <td align="left" valign="top">Нуль или одно вхождение <code>r</code> (только для egrep)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>r1r2</code></td>
      <td align="left" valign="top">За <code>r1</code> следует <code>r2</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>r1|r2</code></td>
      <td align="left" valign="top"><code>r1</code> или <code>r2</code> (только для egrep)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\(r\)</code></td>
      <td align="left" valign="top">Помеченное регулярное выражение <code>r</code> (только для <code>grep</code>); может быть вложенным</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>(r)</code></td>
      <td align="left" valign="top">Регулярное выражение <code>r</code> (только <code>для</code> grep); может быть вложенным</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"/>
      <td align="left" valign="top">Никакое регулярное выражение не соответствует концу строки</td>
     </tr>
    </table>
    <p>Таблица 4.1: <strong>Регулярные выражения <code>grep</code> и <code>egrep</code> (в порядке убывания приоритета)</strong></p>
    <empty-line/>
    <p>Метасимволы <code>^</code> и <code>$</code> привязывают шаблон к началу (<code>^</code>) или концу (<code>$</code>) строки. Например,</p>
    <p><code>$ grep From $MAIL</code></p>
    <p>ищет строки, содержащие <code>From</code> в вашей почтовой посылке, но</p>
    <p><code>$ grep '^From' $MAIL</code></p>
    <p>выдает строки, начинающиеся с <code>From</code>, которые, вероятнее всего, будут заглавными строками сообщений. Метасимволы регулярных выражений пересекаются с метасимволами интерпретатора, поэтому всегда имеет смысл заключать шаблоны команды <code>grep</code> в апострофы.</p>
    <p>Команда <code>grep</code> допускает классы символов, подобные тем, что используются интерпретатором: так, <code>[a-z]</code> задает любую строчную букву. Но есть и различия — если класс символов команды <code>grep</code> начинается с символа слабого ударения то шаблон задает любой символ, кроме входящих в данный класс. Значит, <code>[^0-9]</code> задает любой символ, кроме цифры. Как и в интерпретаторе, обратная дробная черта экранирует символы <code>]</code> и <code>-</code> в классе символов, но команды <code>grep</code> и <code>ed</code> требуют, чтобы эти символы использовались там, где их значение недвусмысленно. Например, шаблон <code>[][-]</code> задает открывающую или закрывающую квадратную скобку либо знак минус.</p>
    <p>Точка <code>'.'</code> эквивалентна <code>'?'</code> в интерпретаторе: она задает любой символ. (Точка, по всей видимости, есть символ, назначение которого различно для разных программ.) Ниже приводятся два примера:</p>
    <p><code>$ ls -l | grep '^d'         </code>Список имен вложенных каталогов</p>
    <p><code>$ ls -l | grep '^.......rw' </code>Список файлов, доступных всем для чтения и записи</p>
    <p>Символ <code>'^'</code> и семь точек задают любые семь символов в начале строки; в случае применения к выходному потоку команды <code>ls -l</code> задается любая строка права доступа.</p>
    <p>Операция "повторитель" (<code>'*'</code>) применима в выражении к предваряющему ее символу или метасимволу (включая класс символов), и вместе они обозначают любое число вхождений символа или метасимвола. Например, <code>x*</code> задает последовательность букв <code>x</code> произвольной длины, <code>[a-zA-Z]*</code> — любую строку букв, <code>.*</code> — все до конца строки, а <code>.*x</code> — все до последнего символа <code>x</code> в строке включительно. Необходимо отметить несколько важных моментов, связанных с повторителем. Во-первых, повторитель действует только на один символ, поэтому <code>xy*</code> соответствует <code>x</code>, за которым идут <code>yy...</code>, но не последовательности типа <code>xyxyxy</code>. Во-вторых, любое число включает нуль, поэтому если вы хотите, чтобы символ присутствовал, в шаблоне его нужно повторить. Например, правильным выражением, задающим строку букв, является такое: <code>[a-zA-Z][a-zA-Z]*</code> (буква, за которой следует нуль или более букв). Регулярное выражение <code>.*</code> соответствует — <code>*</code>, т.е. метасимволу интерпретатора, используемому для имен файлов.</p>
    <p>Ни одно регулярное выражение команды <code>grep</code> не соответствует символу перевода строки; выражения сопоставляются с каждой строкой в отдельности. Регулярные выражения делают команду <code>grep</code> простым языком программирования. Вспомните, что второе поле файла паролей содержит зашифрованный пароль. Приведенная ниже команда проводит поиск пользователей, не имеющих пароля:</p>
    <p><code>$ grep '^[^:]*::' /etc/passwd</code></p>
    <p>Шаблон расшифровывается так: начало строки, любое число символов, отличных от двоеточия, два двоеточия.</p>
    <p>Команда <code>grep</code> — старейшая в семействе программ, к которому относятся команды <code>fgrep</code> и <code>egrep</code>. В основном их действие одинаково, но <code>fgrep</code> может одновременно искать несколько литеральных строк, тогда как <code>egrep</code> интерпретирует настоящие регулярные выражения, подобно <code>grep</code>, но с использованием операций "or" и скобок для группировки выражений, что будет объяснено ниже.</p>
    <p>Обе команды, <code>fgrep</code> и <code>egrep</code>, имеют флаг <code>-f</code> для указания файла, из которого читается шаблон. В этом файле символы перевода строк разделяют шаблоны при параллельном поиске. Допустим, что некоторые слова вы пишете неправильно. В этом случае можно проверить документацию на наличие таких слов, поместив их в файл по одному на строке и воспользовавшись командой <code>fgrep</code>:</p>
    <p><code>$ fgrep -f типичные_ошибки документ</code></p>
    <p>Регулярные выражения, интерпретируемые <code>egrep</code> (они также приведены в табл. 4.1), — те же самые, что и в grep, но с небольшими добавлениями. Можно использовать скобки для группировки, поэтому <code>(xy)*</code> задает пустую строку или любую последовательность <code>xy</code>, <code>xyxy</code>, <code>xyxyxy</code> и т.д. Вертикальная черта <code>|</code> является операцией or (или); <code>today|tomorrow</code> соответствует <code>today</code> или <code>tomorrow</code>, как и <code>to(day|morrow)</code>. Наконец, в команде <code>egrep</code> есть еще две операции повторения: <code>+</code> и <code>?</code>. Шаблон <code>x+</code> задает один или более символов <code>x</code>, а шаблон <code>x?</code> — нуль или один символ <code>x</code> (но не более).</p>
    <p>Команда <code>egrep</code> прекрасно подходит для игр, в которых нужно искать в словаре слова со специальными свойствами. Мы будем обращаться к словарю Вебстера (второе международное издание), хранящемуся в файле в виде списка слов по одному в строке без определений их значения. В вашей системе может быть небольшой словарь <code>/usr/dict/words</code>, предназначенный для проверки правописания; просмотрите его, чтобы выяснить формат. Ниже приведен шаблон, задающий слова английского языка, содержащие все пять гласных в алфавитном порядке:</p>
    <p><code>$ cat alphvowels</code></p>
    <p><code>^[^aeiou]*a[^aeiou]*e[^aeiou]*i[^aeiou]*o[^aeiou]*u[^aeiou]*$</code></p>
    <p><code>$ egrep -f alphvowels /usr/dict/web2 | 3</code></p>
    <p><code>abstemious abstemiously abstentions</code></p>
    <p><code>achelious  acheirous    acleistous</code></p>
    <p><code>affectious annelidous   arsenious</code></p>
    <p><code>arterious  bacterious   caesious</code></p>
    <p><code>facetious  facetiously  fracedinous</code></p>
    <p><code>majestious</code></p>
    <p><code>$</code></p>
    <p>В файле <code>alphvowels</code> шаблон не взят в кавычки. Если применяются кавычки для экранирования шаблона в команде <code>egrep</code>, интерпретатор защищает его от интерпретации командами, но кавычки убирает, и команда <code>egrep</code> никогда "не узнает" о них. Поскольку интерпретатор не заглядывает в файл, кавычки не нужны для защиты содержимого файла. Для этого примера мы могли бы использовать команду <code>grep</code>, но алгоритм <code>egrep</code> таков, что она осуществляет поиск намного быстрее в случае шаблонов с повторителями, особенно при просмотре больших файлов.</p>
    <p>В другом примере требуется найти все английские слова, состоящие из шести или более букв, в которых буквы следуют в алфавитном порядке:</p>
    <p><code>$ cat monotonic</code></p>
    <p><code>^a?b?c?d?e?f?g?h?i?j?k?l?m?n?o?p?r?s?t?u?v?w?x?y?z?$</code></p>
    <p><code>$ egrep -f monotonic /usr/dict/web2 | grep '......' | 5</code></p>
    <p><code>abdest acfcnow adipsy  agnosy almost</code></p>
    <p><code>bedfist behint befcnow bijoux biopsy</code></p>
    <p><code>chintz  dehors dehort  demos  dimpsy</code></p>
    <p><code>egilops ghosty</code></p>
    <p>(Egilops — это болезнь, поражающая пшеницу.) Обратите внимание на использование команды <code>grep</code> для фильтрации выходного потока <code>egrep</code>.</p>
    <p>Для чего нужны три сходные программы? Программа <code>fgrep</code> не интерпретирует метасимволы, но может параллельно обрабатывать тысячи слов (после инициации время ее работы не зависит от числа слов), и поэтому она применяется прежде всего для заданий типа библиографического поиска. Размеры типичных шаблонов для программы <code>fgrep</code> превосходят возможности алгоритмов, используемых в программах <code>grep</code> и <code>egrep</code>. Различия между двумя последними указать труднее. Программа <code>egrep</code> появилась намного раньше. Она интерпретирует регулярные выражения, используемые в командах редактора <code>ed</code>, в ней есть помеченные регулярные выражения и большой набор флагов. Программа <code>egrep</code> интерпретирует более общие выражения (не считая помеченных), и выполняется значительно быстрее (со скоростью, не зависящей от шаблона), но ее стандартная версия требует большего времени на инициацию в случае сложного выражения. Существует новая версия, начинающая работу мгновенно, так что программы <code>egrep</code> и <code>grep</code> теперь можно было бы скомбинировать в одну программу поиска по шаблону.</p>
    <cite>
     <subtitle>Упражнение 4.1</subtitle>
     <p>Прочтите о регулярных выражениях <code>(\(</code> и <code>\))</code> в приложении 1 или справочном руководстве по <code>ed(1)</code>. Используйте программу <code>grep</code> для поиска палиндромов — слов, читающихся одинаково с конца и начала. Подсказка: составьте свой шаблон для слов каждой длины.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 4.2</subtitle>
     <p>Алгоритм программы <code>grep</code> таков: прочесть одну строку, проверить ее на вхождение шаблона, затем продолжить цикл. Как повлияло бы на работу программы то, что регулярные выражения могли бы задавать перевод строки?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>4.2 Другие фильтры</p>
    </title>
    <p>Здесь мы представим вам набор небольших системных фильтров, покажем их возможности и дадим несколько примеров использования. Список этих фильтров далеко не полон — существует еще множество фильтров, входящих в седьмую версию, и, конечно, каждая работающая система имеет свои специфические фильтры. Все стандартные фильтры описаны в разд. 1 справочного руководства по UNIX.</p>
    <p>Рассмотрим сначала программу <code>sort</code>, как наиболее часто используемую. В гл. I было указано ее назначение: сортировка входного потока по строкам в порядке, задаваемом множеством ASCII. Хотя это очевидный порядок для сортировки по умолчанию, существует множество других полезных способов сортировки данных, и программа <code>sort</code> пытается удовлетворить всех, предоставляя множество различных флагов. Например, флаг <code>-f</code> устраняет различие между прописными и строчными буквами, флаг <code>-d</code> (словарный порядок) игнорирует при сравнении все символы, кроме букв, цифр и пробелов.</p>
    <p>Способ сравнения в алфавитном порядке является наиболее распространенным, но иногда требуется произвести сравнение в числовом порядке, флаг <code>-n</code> сортирует по числовому значению, а флаг <code>-r</code> изменяет смысл на противоположный любого условия. Итак, имеем</p>
    <p><code>$ ls | sort -f     </code>Сортировка имен файлов в алфавитном порядке</p>
    <p><code>$ ls -s | sort -n  </code>Сортировка в порядке возрастания размеров файлов</p>
    <p><code>$ ls -s | sort -nr </code>Сортировка в порядке убывания размеров файлов</p>
    <p>Программа <code>sort</code> обычно сортирует целые строки, но ее можно заставить работать только с определенными полями. Обозначение <code>+m</code> показывает, что при сравнении пропускается m полей, а <code>+0</code> обозначает начало строки, например:</p>
    <p><code>$ ls -l | sort +3nr </code>Сортировка по счетчику байтов в порядке убывания</p>
    <p>размеров</p>
    <p><code>$ who | sort +4nr   </code>Сортировка по времени входа в систему, в порядке</p>
    <p>возрастания размеров файлов</p>
    <p>Еще одним полезным флагом программы является <code>-о</code>, задающий имя файла для выходного потока (это может быть один из входных файлов), и флаг <code>-u</code>, который удаляет все, за исключением одной из строк, совпадающих в сортируемых полях. Можно использовать несколько флагов, как показано в примере на странице <code>sort(1)</code> справочного руководства:</p>
    <p><code>$ sort +0f +0 -u filenames</code></p>
    <p>здесь флаг <code>+0f</code> сортирует строку, совмещая строчные и прописные буквы, но идентичные строки могут не быть соседними. Поэтому вводится второй флаг <code>+0</code>, который сортирует одинаковые строки после первой сортировки в обычном порядке ASCII. Наконец, флаг <code>-u</code> выбрасывает все, кроме одной из соседних повторяющихся строк. Таким образом, получив список слов по одному в строке, команда выдает неповторяющиеся слова. Указатель для этой книги был подготовлен с помощью сходной команды <code>sort</code>, обладающей еще большими возможностями (см. руководство по <code>sort(1)</code>).</p>
    <p>Создание команды <code>uniq</code> явилось стимулом для введения флага <code>-u</code> в команде <code>sort</code>: флаг отбрасывает все строки, кроме одной, из группы соседних повторяющихся строк. Выведение отдельной программы для этой операции позволяет выполнять ее независимо от сортировки. Например, <code>uniq</code> удалит повторяющиеся пустые строки, независимо от того, сортируется входной поток или нет. Флаги предусматривают специальные способы обработки повторяющихся строк: <code>uniq -d</code> печатает только повторяющиеся строки, <code>uniq -u</code> — только уникальные, т.е. неповторяющиеся строки; <code>uniq -c</code> подсчитывает число вхождений каждой строки, в чем вскоре вы убедитесь на примере.</p>
    <p>Программа <code>comm</code> служит для сравнения файлов. Получив два отсортированных входных файла <code>f1</code> и <code>f2</code>, она выдает выходной поток в три столбца: строки, встречающиеся только в <code>f1</code>, строки, встречающиеся только в <code>f2</code>, и строки, встречающиеся в обоих файлах. С помощью флага можно убрать любой из этих столбцов:</p>
    <p><code>$ comm -12 f1 f2</code></p>
    <p>выдает только строки, содержащиеся в обоих файлах, а</p>
    <p><code>$ comm -23 f1 f2</code></p>
    <p>выдает строки, которые есть только в первом, но не во втором файле. Это удобно для сравнения каталогов и списка слов со словарем.</p>
    <p>Команда <code>tr</code> проводит транслитерацию символов своего входного потока. Наиболее часто они используются для преобразования строчных букв в прописные и обратно:</p>
    <p><code>$ tr a-z A-Z</code> Перевести строчные буквы в прописные</p>
    <p><code>$ tr A-Z a-z</code> Перевести прописные буквы в строчные</p>
    <p>Несколько отличается от всех рассмотренных выше команд <code>dd</code>. Эта команда предназначена прежде всего для обработки данных на магнитной ленте, полученных из других систем — само ее название служит напоминанием о языке управлений заданиями OS/360. Команда <code>dd</code> выполняет преобразование прописных букв в строчные, и наоборот (в нотации, отличной от нотации команды <code>tr</code>). Она осуществляет перевод из множества символов ASCII в EBCDIC, и наоборот; может читать и писать данные в формате записей фиксированного размера с дополнением пробелами, что характерно для отличных от UNIX систем. На практике команду <code>dd</code> часто используют для работы с исходными неотформатированными данными, откуда бы они ни были получены; она реализует набор средств для работы с двоичными данными.</p>
    <p>Посмотрим, чего можно достичь с помощью взаимодействия фильтров на примере конвейера, который печатает 10 наиболее часто встречающихся во входном потоке слов:</p>
    <p><code>cat $* |</code></p>
    <p><code>tr -sc A-Za-z '\012' |</code> Сжимаем все небуквы в перевод строки</p>
    <p><code>sort |</code></p>
    <p><code>uniq -с |</code></p>
    <p><code>sort -n |</code></p>
    <p><code>tail |</code></p>
    <p><code>5</code></p>
    <p>Команда <code>cat</code> собирает файлы, поскольку <code>tr</code> может читать только стандартный входной поток. Команда <code>tr</code> действует, как указано в справочном руководстве: она сжимает соседние, отличные от букв, символы в символы перевода строк, преобразуя таким образом входной поток в строки из одного слова. Затем слова сортируются и с помощью <code>uniq -с</code> каждая группа идентичных слов сжимается в одну строку, начинающуюся со счетчика, который используется как сортируемое поле в команде <code>sort -n</code>. (Эта последовательность двух команд сортировки, между которыми находится команда uniq, применяется так часто, что уже стала идиомой.) В результате получаются неповторяющиеся слова, отсортированные в порядке возрастания частоты появления в документе. Команда <code>tail</code> отбирает 10 наиболее часто встречающихся слов (т.е. конец отсортированного файла) и команда <code>5</code> печатает их в пять столбцов.</p>
    <p>Заметьте, кстати, что введение символа <code>|</code> в конце строки — это законный способ ее продолжения.</p>
    <cite>
     <subtitle>Упражнение 4.3</subtitle>
     <p>Используя средства этого раздела и файл <code>/usr/dict/words</code>, составьте простой анализатор правильности написания текста на английском языке. Каковы его недостатки и как их исправить?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 4.4</subtitle>
     <p>Напишите программу подсчета слов на предпочитаемом вами языке программирования. Сравните ее размер, скорость и самодокументированность с соответствующим конвейером. Насколько легко вы можете преобразовать эту программу в программу проверки правильности написания текста?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>4.3 Потоковый редактор <code>sed</code></p>
    </title>
    <p>Вернемся теперь к редактору <code>sed</code>. Поскольку он происходит непосредственно от <code>ed</code>, вы легко изучите его и закрепите свои знания о редакторе <code>ed</code>. Основа редактора <code>sed</code> проста:</p>
    <p><code>$ sed 'список команда ed' имена_файлов...</code></p>
    <p>Читаются строки по одной из входных файлов; команды из списка применяются к каждой строке по одной в указанном порядке и результат редактирования записывается в стандартный выходной поток. Например, можно заменить в любом из указанных файлов <code>UNIX</code> на <code>UNIX (TM)</code> с помощью команды:</p>
    <p><code>$ sed 's/\UNIX/\UNIX\ (TM)/g' имена_файлов...&gt; выходной поток</code></p>
    <p>Нужно правильно понимать действие команды. Она не изменяет содержимое своих входных файлов, а лишь пишет в стандартный выходной поток; исходные же файлы не изменяются. Вы уже достаточно разобрались в интерпретаторе, чтобы понять, что</p>
    <p><code>$ sed '...' файл &gt; файл</code></p>
    <p>не лучшее решение: для замены содержимого файлов нужно задействовать временный файл или другую программу. В дальнейшем мы рассмотрим программу, реализующую задачу записи в существующий файл; обратитесь к команде <code>overwrite</code> в (гл. 5).</p>
    <p>Редактор <code>sed</code> выдает все строки автоматически, поэтому команда <code>p</code> не нужна после ввода команды замены, приведенной выше; более того, если она задается, то каждая изменяемая строка печатается дважды. Однако кавычки необходимы почти всегда, поскольку многие метасимволы программы <code>sed</code> имеют специальные значения и для интерпретатора. Рассмотрим, например, команду <code>du -а</code>, порождающую список имен файлов. Обычно она выдает размер и имя файла:</p>
    <p><code>$ du -a ch4*</code></p>
    <p><code>18 ch4.1</code></p>
    <p><code>13 ch4.2</code></p>
    <p><code>14 ch4.3</code></p>
    <p><code>17 ch4.4</code></p>
    <p><code> 2 ch4.9</code></p>
    <p><code>$</code></p>
    <p>Можно использовать <code>sed</code>, чтобы отбросить размеры файлов, но в команде редактирования нужны кавычки для защиты символов <code>*</code> и табуляции от обработки интерпретатором:</p>
    <p><code>$ du -a ch4.* | sed 's/.*&#8594;//'</code></p>
    <p><code>ch4.1</code></p>
    <p><code>ch4.2</code></p>
    <p><code>ch4.3</code></p>
    <p><code>ch4.4</code></p>
    <p><code>ch4.9</code></p>
    <p><code>$</code></p>
    <p>В команде замены удаляются все символы (<code>.*</code>) до крайнего правого символа табуляции включительно (он показан в шаблоне как <code>&#8594;</code>). Аналогичным способом можно выделить из вывода команды who имена пользователей и время входа в систему:</p>
    <p><code>$ who</code></p>
    <p><code>lr  tty1 Sep 29 07:14</code></p>
    <p><code>ron tty3 Sep 29 10:31</code></p>
    <p><code>you tty4 Sep 29 08:36</code></p>
    <p><code>td  tty5 Sep 29 08:47</code></p>
    <p><code>$ who | sed 's/ .* / /'</code></p>
    <p><code>lr  07:14</code></p>
    <p><code>ron 10:31</code></p>
    <p><code>you 08:36</code></p>
    <p><code>td  08:47</code></p>
    <p><code>$</code></p>
    <p>Команда <code>s</code> заменяет пробел и все, что следует за ним (максимально возможно, включая дополнительные пробелы) до следующего пробела на единственный пробел. Кавычки нужны.</p>
    <p>Почти такую же команду <code>sed</code> можно использовать, чтобы создать программу <code>getname</code>, возвращающую имя пользователя:</p>
    <p><code>$ cat getname</code></p>
    <p><code>who am i | sed 's/ .*//'</code></p>
    <p><code>$ getname</code></p>
    <p><code>you $</code></p>
    <p>Другая команда <code>sed</code> применяется настолько часто, что мы поместили ее в командный файл с именем <code>ind</code>. Эта команда вставляет пробелы до шага табуляции; она удобна для лучшего расположения текста при печати.</p>
    <p>Реализовать команду <code>ind</code> просто: достаточно установить символ табуляции в начале каждой строки:</p>
    <p><code>$ sed 's/^/&#8594;/' $*</code> Первая версия ind</p>
    <p>В этой версии символ табуляции вставляется даже в пустую строку, что не требуется. В лучшей версии используется возможность <code>sed</code> выбирать строки для модификации. Если команде предшествует шаблон, то изменяться будут только строки, соответствующие шаблону:</p>
    <p><code>sed '/./s/^/&#8594;/' $*</code> Вторая версия</p>
    <p>Шаблон <code>/./</code> задает любую строку, в которой есть по крайней мере один символ, кроме символа перевода строки, поэтому команда <code>s</code> не выполняется для пустых строк. Вспомним, что <code>sed</code> выдает все строки, независимо от того, менялись они или нет, так что пустые строки по-прежнему выдаются.</p>
    <p>Есть еще один способ определения команды <code>ind</code>. Можно выполнять команды только для тех строк, которые не соответствуют выбираемому шаблону, предварив команду знаком восклицания <code>'!'</code>. В команде</p>
    <p><code>sed '/^$/!s/^/&#8594;/' $*</code> Третья версия</p>
    <p>шаблон <code>/^$/</code> задает пустые строки (перевод строки сразу следует за ее началом), поэтому <code>/^$/!</code> предписывает не выполнять команду для пустых строк.</p>
    <p>Как уже отмечалось, <code>sed</code> печатает каждую строку автоматически, независимо от того, какие операции над ней выполнялись (если только она не была удалена). Кроме того, можно использовать большинство команд редактора <code>ed</code>. Поэтому легко составить программу <code>sed</code>, которая напечатает, скажем, три первых строки входного потока, а затем завершится:</p>
    <p><code>sed 3q</code></p>
    <p>Хотя <code>3q</code> не является законной командой <code>ed</code>, для <code>sed</code> она имеет смысл: копировать строки и завершить выполнение после третьей.</p>
    <p>От вас может потребоваться другая работа с данными, например вставка пробелов. Один из способов заключается в том, чтобы выходной поток <code>sed</code> пропустить через команду <code>ind</code>, но поскольку редактор <code>sed</code> допускает несколько команд, можно сделать это путем одного обращения к <code>sed</code> (хотя и несколько необычного):</p>
    <p><code>sed 'S/^/&#8594;/</code></p>
    <p><code>3q'</code></p>
    <p>Обратите внимание, где находятся кавычки и символ перевода строки: команды должны быть на отдельных строках, но редактор игнорирует пробелы и символы табуляции в начале строки.</p>
    <p>Представляется естественным с помощью рассмотренных выше приемов составить программу <code>head</code>, которая будет печатать несколько строк из каждого своего файла-аргумента. Но <code>sed 3q</code> (или <code>10q</code>) настолько просто задать, что в этом никогда не возникало потребности. Однако мы ввели команду <code>ind</code>, так как соответствующая последовательность для <code>sed</code> длиннее. (В процессе работы над книгой мы заменили существовавшую программу на языке Си в 30 строк на одну строку команды <code>ind</code> версии 2, приведенной выше.) Четкого критерия в отношении того, когда имеет смысл создавать отдельную программу из сложной командной строки, нет, поэтому мы предлагаем вам свое решение: поместите программу в свой каталог <code>/bin</code> и посмотрите, будете ли вы ее действительно применять.</p>
    <p>Можно помещать команды редактора <code>sed</code> в файл и выполнять их, получая оттуда с помощью обращения:</p>
    <p><code>$ sed -f командный_файл</code></p>
    <p>Вы можете обращаться к строкам, используя не только их номера. Так, команда</p>
    <p><code>$ sed '/шаблон/q'</code></p>
    <p>выдает из входного потока все строки до первой включительно, которые соответствуют шаблону, а команда</p>
    <p><code>$ sed '/шаблон/d'</code></p>
    <p>удаляет каждую строку, содержащую шаблон; удаление происходит до автоматического вывода строк, поэтому удаленные строки не учитываются.</p>
    <p>Хотя автоматический вывод обычно удобен, иногда он мешает. Его можно отключить с помощью флага <code>-n</code>; в этом случае в выходном потоке появятся только строки, задаваемые явной командой вывода p. Например,</p>
    <p><code>$ sed -n '/шаблон/p'</code></p>
    <p>эквивалентен команде <code>grep</code>. Условие сопоставления можно инвертировать, если завершить его символом <code>!</code>, поэтому</p>
    <p><code>$ sed -n '/шаблон/!p'</code></p>
    <p>эквивалентно команде <code>grep -v</code>. (Так же, как <code>sed '/шаблон/d'</code>.)</p>
    <p>Для чего нужны две команды <code>sed</code> и <code>grep</code>? В конце концов, <code>grep</code> — всего лишь частный случай команды <code>sed</code>. Это в какой-то степени объясняется историческими причинами: команда <code>grep</code> появилась намного раньше, чем команда <code>sed</code>. Но она не только уцелела, но и активно применялась. В силу специфики назначения обеих команд <code>grep</code> значительно проще использовать, чем команду <code>sed</code>, так как ее использование в типичных ситуациях настолько лаконично, насколько возможно (к тому же у нее есть возможности, отсутствующие у команды <code>sed</code>; см., например, описание флага <code>-b</code>). Но все-таки программы могут "умирать". Когда-то была программа с именем <code>gres</code>, выполняющая простую подстановку, но она исчезла почти мгновенно, когда появилась команда <code>sed</code>.</p>
    <p>Используя запись, такую же, как в редакторе <code>ed</code>, можно вставлять символы перевода строк с помощью команды <code>sed</code>:</p>
    <p><code>$ sed '/$/\</code></p>
    <p><code>&gt; /'</code></p>
    <p>Здесь добавляется символ перевода строки к каждой строке, и таким образом пустые строки вставляются во входной поток, а команда</p>
    <p><code>$ sed 's/[&#8594;][&#8594;]*/\</code></p>
    <p><code>&gt;/g'</code></p>
    <p>заменяет каждую последовательность пробелов или символов табуляции на символ перевода строки, т. е. разбивает входной поток на строки из одного слова. (Регулярное выражение <code>'[&#8594;]'</code> задает пробел или символ табуляции, <code>'[&#8594;]*'</code> задает нуль или более таких символов, а весь шаблон — один или более пробелов и/или символов табуляции.)</p>
    <p>Можно также использовать пару регулярных выражений или номеров строк для задания диапазона строк, к которому будет применяться произвольная команда.</p>
    <p><code>$ sed -n '20,30p'       </code>Печать только строк с 20-й по 30-ю</p>
    <p><code>$ sed '1,10d'           </code>Удаление строк с 1-й до 10-й (=tail +11)</p>
    <p><code>$ sed '1,/^$/cd'        </code>Удаление всех строк до первой пустой включительно</p>
    <p><code>$ sed -n '/^$/,/^end/p' </code>Печать всех групп строк, начиная от пустой строки до</p>
    <p>строки, начинающейся с end</p>
    <p><code>$ sed '$d'              </code>Удаление последней строки</p>
    <p>Строки нумеруются с начала входного потока; обнуление не происходит с началом нового файла.</p>
    <p>У команды <code>sed</code> есть существенное ограничение, которое, однако, отсутствует в редакторе <code>ed</code>: в ней поддерживается относительная нумерация строк. В частности, операции <code>+</code> и <code>-</code> не действуют в выражениях, задающих номера строк, поэтому невозможно двигаться назад во входном потоке:</p>
    <p><code>$ sed '$-1d'</code> Недопустима обратная адресация</p>
    <p><code>Unrecognized command: $-1d</code></p>
    <p><code>$</code></p>
    <p>Если строка считана, предыдущая исчезла навсегда: нет способа специфицировать предыдущую строку, а именно это требуется в команде. В принципе такой способ есть в команде <code>sed</code>, но он слишком изощренный. (См. команду <code>hold</code> в справочном руководстве по UNIX.) Невозможна и относительная прямая адресация:</p>
    <p><code>$ sed '/что-то/+1d'</code> Недопустима прямая адресация</p>
    <p>Редактор <code>sed</code> имеет возможность записывать в несколько выходных файлов. Например, команда</p>
    <p><code>$ sed -n '/шабл/w файл1</code></p>
    <p><code>&gt; /шабл/!w файл2' имена_файлов...</code></p>
    <p><code>$</code></p>
    <p>записывает строки, соответствующие <code>"шабл"</code>, в <code>файл1</code>, а не соответствующие — в <code>файл2</code>, или, если вернуться к нашему первому примеру:</p>
    <p><code>$ sed 's/\UNIX(TM)/gw u.out' имена_файлов...&gt; выход</code></p>
    <p>то здесь, как и ранее, весь выходной поток записывается в файл <code>"выход"</code>, но к тому же измененные строки записываются в файл <code>u.out</code>.</p>
    <p>Иногда нужна помощь со стороны интерпретатора, чтобы в команду редактора включить аргументы командного файла. Одним из примеров служит программа <code>newer</code>, которая выдает все более новые, чем заданный, файлы каталога:</p>
    <p><code>$ cat newer</code></p>
    <p><code># newer f: список файлов, созданных после f</code></p>
    <p><code>ls -t | sed '/'$1'$/q'</code></p>
    <p><code>$</code></p>
    <p>Кавычки защищают различные специальные символы, предназначенные для редактора, оставляя <code>$1</code> открытым для интерпретатора, чтобы он заменил его на имя файла. Существует альтернативный способ записи аргумента:</p>
    <p><code>"/^$1\$/q"</code></p>
    <p>так как <code>$1</code> заменяется на аргумент, тогда как <code>\$</code> становится просто <code>$</code>.</p>
    <p>Аналогично можно составить программу <code>older</code>, которая выдает в качестве параметра все файлы, более старые, чем заданный:</p>
    <p><code>$ cat older</code></p>
    <p><code># older f: список файлов, созданных ранее f</code></p>
    <p><code>ls -tr | sed '/'$1'$/q'</code></p>
    <p><code>$</code></p>
    <p>Единственное различие состоит в применении флага <code>-r</code> в команде <code>ls</code> для изменения порядка выдачи файлов.</p>
    <p>Хотя редактор <code>sed</code> способен на гораздо большее, чем мы вам продемонстрировали, включая проверку условий, циклы и ветвления, запоминание предыдущих строк, и, конечно, в нем допустимы многие команды редактора <code>ed</code>, описанные в приложении 1. Тем не менее в основном sed используется так, как было показано; одна или две простые команды редактирования, а не длинные и сложные последовательности. В табл. 4.2 собраны некоторые команды <code>sed</code>, хотя и не приведены операции над несколькими строками.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>a\</code></td>
      <td align="left" valign="top">Добавлять строки к выходному потоку, пока одна из них не закончится на <code>\</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>b label</code></td>
      <td align="left" valign="top">Перейти на команду: <code>label</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>c\</code></td>
      <td align="left" valign="top">Заменить строки на последующий текст, как в команде <code>a</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>d</code></td>
      <td align="left" valign="top">Удалить строку; прочесть следующую входную строку</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>i\</code></td>
      <td align="left" valign="top">Вставить последующий текст перед следующим выходным потоком</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>l</code></td>
      <td align="left" valign="top">Выдать строку, напечатав все невидимые символы</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>p</code></td>
      <td align="left" valign="top">Выдать строку</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>q</code></td>
      <td align="left" valign="top">Выйти</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>r file</code></td>
      <td align="left" valign="top">Читать <code>file</code>, содержимое его переслать в выходной поток</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>s/old/new/f</code></td>
      <td align="left" valign="top">Заменить <code>old</code> на <code>new</code>. Если <code>f</code>=<code>g</code>, заменить все вхождения; <code>f</code>=<code>p</code>, вывод; <code>f</code>=<code>w</code> файл, запись в файл</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>t label</code></td>
      <td align="left" valign="top">Проверка: переход на метку, если была замена в текущей строке</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>w file</code></td>
      <td align="left" valign="top">Записать строку в файл</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>y/str1/str2/</code></td>
      <td align="left" valign="top">Заменить каждый символ строки <code>str1</code> на соответствующий символ строки <code>str2</code> (диапазоны недопустимы)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>=</code></td>
      <td align="left" valign="top">Выдать текущую нумерацию входной строки</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>!cmd</code></td>
      <td align="left" valign="top">Выполнить команду <code>sed cmd</code>, только если строка не выбрана</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>: label</code></td>
      <td align="left" valign="top">Установить метку для команд <code>b</code> и <code>t</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>{</code></td>
      <td align="left" valign="top">Команды до соответствующей скобки <code>}</code> рассматривать как группу</td>
     </tr>
    </table>
    <p>Таблица 4.2: <strong>Сводка команд <code>sed</code></strong></p>
    <empty-line/>
    <p>Редактор <code>sed</code> удобен потому, что позволяет работать с произвольно длинными входными строками. Это "быстрый" редактор, который сходен с редактором <code>ed</code> в интерпретации регулярных выражений и в обработке отдельных строк. Однако, с другой стороны, его возможности запоминания ограничены (трудно запомнить текст от одной строки до другой) — делается только один проход по данным, нельзя двигаться назад, нет способов прямой адресации типа <code>/.../+1:</code> и нет средств для работы с числами, т.е. он является чисто текстовым редактором.</p>
    <cite>
     <subtitle>Упражнение 4.5</subtitle>
     <p>Измените команды <code>older</code> и <code>newer</code> так, чтобы они не включали файл-аргумент в свой выходной поток. Измените их так, чтобы файлы выдавались в обратном порядке.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 4.6</subtitle>
     <p>С помощью редактора <code>sed</code> сделайте программу <code>bundle</code> совершенно надежной. Подсказка: в конструкции "документ здесь" слово, отмечающее конец данных, распознается только в том случае, когда оно совпадает со строкой полностью.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>4.4 Язык <code>awk</code> поиска и обработки шаблонов</p>
    </title>
    <p>Некоторые ограничения <code>sed</code> преодолены в программе <code>awk</code>. Принцип работы этой программы сходен с принципом работы программы <code>sed</code>, но синтаксически она ближе к языку программирования Си, чем к текстовому редактору. Способ задания команды такой же, как и для <code>sed</code>:</p>
    <p><code>$ awk 'программа' имена_файлов...</code></p>
    <p>но <emphasis>программа</emphasis> другая:</p>
    <p><code>шаблон {действие}</code></p>
    <p><code>шаблон {действие}</code></p>
    <p><code>...</code></p>
    <p>Программа <code>awk</code> читает входной поток по одной строке из указанных файлов. Строки сопоставляются с шаблонами по порядку; для каждого шаблона, соответствующего строке, выполняется необходимое действие. Как и в редакторе <code>sed</code>, входные файлы здесь не изменяются.</p>
    <p>Шаблоны могут быть регулярными выражениями в <code>sed</code> или более сложными условиями, напоминающими язык Си. Приведем простой пример (такого же результата можно добиться с помощью команды <code>egrep</code>):</p>
    <p><code>$ awk '/регулярное_выражение/ {print}' имена_файлов...</code></p>
    <p>Печатается каждая строка, соответствующая регулярному выражению.</p>
    <p>Шаблоны или действия могут отсутствовать. Если отсутствует действие, то по умолчанию печатаются строки, соответствующие шаблону, поэтому команда</p>
    <p><code>$ awk '/регулярное_выражение/' имена_файлов...</code></p>
    <p>эквивалентна предыдущей. Наоборот, если отсутствует шаблон, то действие выполняется для каждой входной строки. Следовательно, команда</p>
    <p><code>$ awk '{print}' имена_файлов...</code></p>
    <p>дает те же результаты, что и команда <code>cat</code>, хотя действует медленнее.</p>
    <p>Теперь перейдем к более интересным примерам, но прежде сделаем одно замечание. Как и в случае <code>sed</code>, программу команды <code>awk</code> можно получать из файла:</p>
    <p><code>$ awk -f кмд файл имена_файлов...</code></p>
    <p>Поля. В программе <code>awk</code> каждая входная строка автоматически разбивается на поля, т.е. последовательности символов без пробелов, разделенные пробелами и символами табуляции. По этому определению выходной поток команды <code>who</code> имеет пять полей:</p>
    <p><code>$ who</code></p>
    <p><code>you tty2 sep 29 11:53</code></p>
    <p><code>jim tty4 sep 29 11:27</code></p>
    <p><code>$</code></p>
    <p>Поля обозначаются как <code>$1</code>, <code>$2</code>, …, <code>$NF</code>, где <code>NF</code> — переменная, значение которой установлено равным числу полей. В нашем случае <code>NF=5</code> для обеих строк. (Учтите разницу между <code>NF</code>, числом полей и <code>$NF</code> — последним полем строки. В отличие от интерпретатора в программе <code>awk</code> только номера полей начинаются с <code>$</code>; переменные не имеют такого префикса.) Например, следующая команда выдаст поле "размер файла" из результата выполнения команды <code>du -а</code></p>
    <p><code>$ du -a | awk '{print $2}'</code></p>
    <p>а для печати имен пользователей, работающих в системе, и времени входа нужно задать:</p>
    <p><code>$ who awk '{print $1, $5}'</code></p>
    <p><code>you 11:53</code></p>
    <p><code>jim 11:27 $</code></p>
    <p>Для печати имени и времени входа в систему, упорядоченных по времени, зададим:</p>
    <p><code>$ who awk '{print $5, $1}' | sort</code></p>
    <p><code>11:27 jim</code></p>
    <p><code>11:53 you</code></p>
    <p><code>$</code></p>
    <p>Это альтернативные решения примеров, приведенных выше в данной главе, в которых использовалась команда <code>sed</code>. Хотя с программой <code>awk</code> проще работать в подобных случаях, она обычно выполняется медленнее как в начальной фазе, так и при большом входном потоке.</p>
    <p>Обычно предполагается, что поля разделяются произвольным числом пробелов и символов табуляций, но можно определить в качестве разделителя любой одиночный символ. Один из способов состоит в задании в командной строке флага <code>-F</code> (здесь прописная буква). Например, поля в файле паролей <code>/etc/passwd</code> разделяются двоеточиями:</p>
    <p><code>$ sed 3q /etc/passwd</code></p>
    <p><code>root:3D.fHR5KoB.3s:0:1:S.User:/:</code></p>
    <p><code>ken:y.68wdl.ijayz:6:1:K.Thompson:/usr/ken:</code></p>
    <p><code>dmr:z4u3dJWbg7wCk:7:1:D.M.Ritchie:/usr/dmr:</code></p>
    <p><code>$</code></p>
    <p>Для печати имен пользователей, образующих первое поле, можно задать:</p>
    <p><code>$ sed 3q /etc/passwd | awk -F : '{print $1}'</code></p>
    <p><code>root</code></p>
    <p><code>ken</code></p>
    <p><code>dmr</code></p>
    <p>Обработка пробелов и символов табуляции здесь особая. По умолчанию и пробелы, и символы табуляции служат разделителями, а разделители в начале строки отбрасываются. Однако если в качестве разделителя определен не пробел, то разделители в начале строки учитываются при определении полей. В частности, если используется символ табуляции, то пробелы не являются символами разделителями, пробелы в начале строки вводят в поле, и каждый символ табуляции определяет поле.</p>
    <subtitle>Печать</subtitle>
    <p>В программе <code>awk</code>, помимо числа входных полей, доступна и другая интересная информация. Встроенная переменная <code>NR</code> хранит номер текущей входной "записи", т.е. строки. Поэтому для вставки номера строки перед строкой входного потока достаточно задать:</p>
    <p><code>$ awk '{print NR, $0}'</code></p>
    <p>Поле <code>$0</code> обозначает всю входную строку без изменений. В операторе <code>print</code> фрагменты, отделяемые запятой, печатаются через символы разделения полей выходного потока, которые по умолчанию служат пробелами.</p>
    <p>Формат печати оператора <code>print</code> обычно является <code>приемлемым</code>. При несоответствующем формате используйте оператор <code>printf</code>, обеспечивающий полный контроль над выходным потоком. Например, для печати номеров строк в поле размером в четыре цифры можно задать такую команду:</p>
    <p><code>$ awk '{printf "%4d %s\n", NR, $0}'</code></p>
    <p>Выражение <code>%4</code> задает десятичное целое число (<code>NR</code>) в поле размером в четыре цифры, <code>%S</code> — строка символов (<code>$0</code>), <code>\n</code> — символ перевода строки, который нужен потому, что оператор <code>printf</code> не выдает автоматически пробелы или символы перевода строк. Оператор <code>printf</code> сходен с аналогичной Си функцией (см. справочное руководство по <code>printf(3)</code>).</p>
    <p>Мы могли бы определить программу <code>ind</code> (рассматривавшуюся в начале главы) следующим образом:</p>
    <p><code>$ awk '{printf "\t%s\n", $0}' $*</code></p>
    <p>Здесь выдается символ табуляции (<code>\t</code>) и входная строка.</p>
    <subtitle>Шаблоны</subtitle>
    <p>Предположим, что вы хотите найти в файле <code>/etc/passwd</code> пользователей, не имеющих пароля. Зашифрованный пароль находится во втором поле, поэтому программа состоит из одного шаблона:</p>
    <p><code>$ awk -F: '$2 == ""' /etc/passwd</code></p>
    <p>Шаблон проверяет, является ли второе поле пустой строкой (операция <code>==</code> — это проверка на равенство).</p>
    <p>Такой шаблон можно задать различными способами:</p>
    <p><code>$2==""          </code>Второе поле пусто</p>
    <p><code>$2~/^$/         </code>Второе поле соответствует пустой строке</p>
    <p><code>$2!~/./         </code>Второе поле не содержит ни одного символа</p>
    <p><code>length($2) == 0 </code>Длина второго поля равна нулю</p>
    <p>Символ <code>~</code> обозначает соответствие регулярному выражению, а символ <code>!</code> — отсутствие соответствия. Само регулярное выражение заключено в символы дробной черты.</p>
    <p>Встроенная функция <code>length</code> программы awk вычисляет длину строки символов. Шаблону может предшествовать символ ! для отрицания его, например,</p>
    <p><code>!($2=="")</code></p>
    <p>Операция <code>!</code> подобна такой же операции в языке Си, но в редакторе <code>sed</code> эта операция следует за шаблоном.</p>
    <p>Наиболее типичное использование шаблонов в программе <code>awk</code> сводится к задачам простой проверки данных. Большинство из них немногим сложнее, чем поиск строк, не удовлетворяющих какому-то критерию; если нет выходного потока, то считается, что данные удовлетворяют соответствующему критерию (по принципу "отсутствие новостей — хорошая новость"). Например, в следующем шаблоне проверяется с помощью операции <code>%</code>, вычисляющей остаток от деления, четно или не четно число полей в каждой входной строке:</p>
    <p><code>$ NF % 2 != 0 # напечатать, если нечетное число полей</code></p>
    <p>Другой шаблон выдает исключительно длинные строки, используя встроенную функцию <code>length</code>:</p>
    <p><code>length ($0) &gt;72 # напечатать, если слишком длинная строка</code></p>
    <p>В программе <code>awk</code> используется то же соглашение о комментарии, что и в интерпретаторе: символ <code>#</code> отмечает начало комментария.</p>
    <p>Можно сделать выходной поток более информативным, снабдив его предупреждающим сообщением и частью слишком длинной строки, используя для этого встроенную функцию <code>substr</code>:</p>
    <p><code>length($0) &gt; 72 {print "Строка", NR, "длинная" : substr($0, 1, 60)}</code></p>
    <p>Функция <code>substr(s, m, n)</code> выделяет подстроку из строки s, начинающуюся с символа с номером <code>m</code> и длиной в <code>n</code> символов. (Символы в строке нумеруются с 1.) Если <code>n</code> отсутствует, то берется подстрока от <code>m</code> до конца строки. Эту функцию можно использовать для выделения полей с фиксированным положением, например выделить время в часах и минутах из результата выполнения команды <code>date</code>:</p>
    <p><code>$ date</code></p>
    <p><code>Thu Sep 29 12:17:01 EDT 1983</code></p>
    <p><code>$ date | awk '{print substr($4, 1, 5) }'</code></p>
    <p><code>12:17</code></p>
    <p><code>$</code></p>
    <cite>
     <subtitle>Упражнение 4.7</subtitle>
     <p>Сколько различных программ <code>awk</code> вы можете составить для переписи входного потока в выходной, как это делает команда <code>cat</code>? Какая из них самая короткая?</p>
    </cite>
    <subtitle>Шаблоны <code>BEGIN</code> и <code>END</code></subtitle>
    <p>Программа <code>awk</code> имеет два специальных шаблона <code>BEGIN</code> и <code>END</code>. Действия, соответствующие <code>BEGIN</code>, выполняются прежде, чем читается первая входная строка; можно использовать этот шаблон для инициации переменных, печати заголовков или для установки символа разделителя полей, присваивая его переменной <code>FS</code>.</p>
    <p><code>$ awk 'BEGIN { FS = ":" }</code></p>
    <p><code>&gt; $2 == "" ' /etc/paswd</code></p>
    <p><code>$</code> Результата нет: все работают с паролями</p>
    <p>Действия, указанные в шаблоне <code>END</code>, выполняются после обработки последней входной строки:</p>
    <p><code>$ awk 'END {print NR}'...</code></p>
    <p>Здесь печатается число строк входного потока.</p>
    <subtitle>Арифметика и переменные</subtitle>
    <p>До сих пор в примерах выполнялись только простые операции с текстом. Достоинством программы <code>awk</code> является ее возможность попутно проводить вычисления над входными данными: что-нибудь подсчитать, вычислить суммы и средние значения и т.п. Типичный пример таких вычислений — подсчет суммы столбца чисел. Так, следующая команда складывает все числа первого столбца</p>
    <p><code>{s=s+$1}</code></p>
    <p><code>END {print s}</code></p>
    <p>Поскольку число значений доступно с помощью переменной <code>NR</code>, изменив последнюю строку на</p>
    <p><code>END {print s, s/NR}</code></p>
    <p>мы получим и сумму, и среднее значение.</p>
    <p>Этот пример показывает, как используются переменные в awk. Переменная <code>s</code> не является встроенной, она определяется самим фактом использования. По умолчанию переменные инициируются нулем, так что, как правило, не нужно беспокоиться об их инициации.</p>
    <p>В программе <code>awk</code> есть такие же сокращенные формы арифметических операторов, как и в языке Си, поэтому естественная запись примера имела бы вид:</p>
    <p><code>{s+=$1}</code></p>
    <p><code>END {print}</code></p>
    <p>Запись <code>s+=$1</code> равноценна записи <code>s=s+$1</code>, но более компактна. Можно обобщить пример по подсчету входных строк:</p>
    <p><code>    { nc+=length($0) +1 # число символов, +1 для \n</code></p>
    <p><code>     nw += NF           # число слов</code></p>
    <p><code>    }</code></p>
    <p><code>END {print NR, nw, nc }</code></p>
    <p>Здесь подсчитывается число строк, слов и символов входного потока, т.е. выполняются те же действия, что и по команде <code>wc</code> (хотя она и не разбивает общую сумму по файлам).</p>
    <p>В качестве другого примера выполнения арифметических операций рассмотрим программу, подсчитывающую число страниц по 66 строк в каждой. Страницы получаются при прогоне несколько файлов через команду <code>pr</code>. Это можно оформить в виде команды <code>prpages</code>:</p>
    <p><code>$ cat prpages</code></p>
    <p><code># prpages: подсчет числа страниц, выдаваемых pr</code></p>
    <p><code>wc $* |</code></p>
    <p><code>awk '!/total$/ { n += int(($1+55)/56) }</code></p>
    <p><code>     END       { print n }'</code></p>
    <p><code>$</code></p>
    <p>Команда <code>pr</code> помещает на каждую страницу 56 строк текста (это число определяется эмпирически). Для каждой строки вывода команды <code>wc</code>, которая не содержит слово <code>total</code> в конце строки, число страниц округляется, а затем выделяется целая часть с помощью встроенной функции <code>int</code>.</p>
    <p><code>$ wc ch4.*</code></p>
    <p><code> 753  3090 18129 ch4.1</code></p>
    <p><code> 612  2421 13242 ch4.2</code></p>
    <p><code> 637  2462 13455 ch4.3</code></p>
    <p><code> 802  2986 16904 ch4.4</code></p>
    <p><code>  50   213  1117 ch4.9</code></p>
    <p><code>2854 11172 62847 total</code></p>
    <p><code>$ prpages ch4.*</code></p>
    <p><code>53</code></p>
    <p><code>$</code></p>
    <p>Для проверки этого результата запустим команды <code>pr</code> и <code>awk</code> одновременно:</p>
    <p><code>$ pr ch4.* | awk 'END {print NR/66}'</code></p>
    <p><code>53</code></p>
    <p><code>$</code></p>
    <p>Переменные программы <code>awk</code> могут также хранить строки символов. Рассматривать ли переменную как число или как строку символов — зависит от контекста. Грубо говоря, в арифметических выражениях типа <code>s+=$1</code> используется числовое значение в контексте операций со строками типа <code>x=="abc"</code> — строковое значение в неясных случаях, например <code>x&gt;y</code>, — строковое значение, если только операнды не являются явно числовыми. (Правила четко сформулированы в справочном руководстве по применению команды <code>awk</code>.) Строковые переменные инициируются пустой строкой. В последующих разделах строки будут активно использоваться.</p>
    <p>В программе <code>awk</code> есть несколько своих встроенных переменных обоих типов, таких, как <code>NR</code> и <code>FS</code>. Их полный список приведен в табл. 4.3, а в табл. 4.4 перечислены операции, выполняемые командой.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>FILENAME</code></td>
      <td align="left" valign="top">Имя текущего входного файла</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>FS</code></td>
      <td align="left" valign="top">Символ разделения полей (по умолчанию приняты пробел и символ табуляции)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>NF</code></td>
      <td align="left" valign="top">Число полей входной строки</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>NR</code></td>
      <td align="left" valign="top">Число входных строк</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>OFMT</code></td>
      <td align="left" valign="top">Формат вывода чисел (по умолчанию принят <code>%g</code>; обратитесь к руководству по <code>printf(3y)</code>)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>OFS</code></td>
      <td align="left" valign="top">Строка разделитель полей в выходном потоке (пробел по умолчанию)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ORS</code></td>
      <td align="left" valign="top">Строка-разделитель строк в выходном потоке (символ перевода строки по умолчанию)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>RS</code></td>
      <td align="left" valign="top">Символ разделения входных строк (символ перевода строки по умолчанию)</td>
     </tr>
    </table>
    <p>Таблица 4.3: <strong>Встроенные переменные <code>awk</code></strong></p>
    <empty-line/>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>= += -= /= %=</code></td>
      <td align="left" valign="top">Присваивание; <code>v ор=expr</code> есть <code>v=v op (expr)</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>||</code></td>
      <td align="left" valign="top">ИЛИ: <code>expr1 || expr2</code> истина, если одно или оба истинны; <code>expr2</code> не вычисляется, если <code>expr1</code> истинна</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>&amp;&amp;</code></td>
      <td align="left" valign="top">И: <code>expr1 &amp;&amp; expr2</code> истина, если оба истинны; <code>expr2</code> не вычисляется, если expr1 ложь</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>!</code></td>
      <td align="left" valign="top">Отрицание значения выражения</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>&gt;&gt;= &lt;&lt;= == != ~ !~</code></td>
      <td align="left" valign="top">Операция отношения; <code>!</code> и <code>!~</code> это соответствие и несоответствие</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>пусто</code></td>
      <td align="left" valign="top">Конкатенация строк</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>+ -</code></td>
      <td align="left" valign="top">Сложение, вычитание</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>* / %</code></td>
      <td align="left" valign="top">Умножение, деление, вычисление остатка</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>++ --</code></td>
      <td align="left" valign="top">Увеличение, уменьшение (префиксное или постпрефиксное)</td>
     </tr>
    </table>
    <p>Таблица 4.4: <strong>Операции, выполняемые <code>awk</code> (в порядке возрастания приоритета)</strong></p>
    <empty-line/>
    <cite>
     <subtitle>Упражнение 4.8</subtitle>
     <p>Наша проверка программы <code>prpages</code> подсказывает иную реализацию этой программы. Поэкспериментируйте, чтобы выяснить, какая из них выполняется быстрее.</p>
    </cite>
    <subtitle>Управление</subtitle>
    <p>При редактировании большого файла очень легко (судя по опыту) случайно создать копию соседнего слова, что практически никогда не происходит преднамеренно. Для устранения таких ошибок в семействе программ Writers Workbench (рабочий набор редактора) существует программа <code>double</code>, отыскивающая пары идентичных соседних слов. Ниже показана реализация этой программы с помощью <code>awk</code>:</p>
    <p><code>$ cat double</code></p>
    <p><code>awk '</code></p>
    <p><code>FILENAME != prevfile { # new file</code></p>
    <p><code> NR = 1                # reset line number</code></p>
    <p><code> prevfile = FILENAME</code></p>
    <p><code>}</code></p>
    <p><code>NF &gt; 0 {</code></p>
    <p><code> if ($1 == lastword)</code></p>
    <p><code>  printf "double %s, file %s, line %d\n" ,$1, FILENAME, NR</code></p>
    <p><code> for (i = 2; i &lt;= NF; i++)</code></p>
    <p><code>  if ($i == $(i-1))</code></p>
    <p><code>   printf "double %s, file %s, line %d\n", $i, FILENAME, NR</code></p>
    <p><code> if (NF &gt; 0)</code></p>
    <p><code>  lastword = $NF</code></p>
    <p><code>}' $*</code></p>
    <p><code>*</code></p>
    <p><code>$</code></p>
    <p>Операция <code>++</code> означает автоувеличение операнда, а операция <code>--</code> — его автоуменьшение.</p>
    <p>Встроенная переменная <code>FILENAME</code> хранит имя текущего входного файла. Поскольку в переменной <code>NR</code> подсчитывается число строк с начала входного потока, мы изменяем ее значение всякий раз при изменении имени файла, чтобы точно указать строку с двойником.</p>
    <p>Оператор <code>if</code> — такой же, как в языке Си:</p>
    <p><code>if (условие)</code></p>
    <p><code> оператор1</code></p>
    <p><code>else</code></p>
    <p><code> оператор2</code></p>
    <p>Если условие верно, то выполняется <code>оператор1</code>; если оно ложно и если альтернативная часть присутствует, то выполняется <code>оператор2</code>. Альтернативная часть не обязательна.</p>
    <p>Цикл <code>for</code> аналогичен таковому в языке Си, но отличается от цикла в языке <code>shell</code>:</p>
    <p><code>for (выражение1; условие; выражение2)</code></p>
    <p><code> оператор</code></p>
    <p>Цикл <code>for</code> идентичен приведенному ниже оператору, который также допустим в программе <code>awk</code>:</p>
    <p><code>Выражение1 while (условие) {</code></p>
    <p><code> оператор</code></p>
    <p><code> выражение2</code></p>
    <p><code>}</code></p>
    <p>Например, конструкция</p>
    <p><code>for (i=2; i &lt;= NF; i++)</code></p>
    <p>является циклом с <code>i</code>, принимающим значения 2, 3 и т.д., включая число полей <code>NF</code>.</p>
    <p>Оператор <code>break</code> вызывает немедленный выход из цикла <code>while</code> или <code>for</code>; оператор <code>continue</code> инициирует переход к следующему шагу цикла (к условию в операторе <code>while</code> или к <code>выражению2</code> в операторе <code>for</code>). Оператор <code>next</code> вызывает чтение следующей входной строки и сопоставление ее с шаблонами с начала программы <code>awk</code>, а оператор <code>exit</code> — немедленный переход на действия, определенные в шаблоне <code>END</code>.</p>
    <subtitle>Массивы</subtitle>
    <p>Как и в большинстве языков программирования, в <code>awk</code> есть массивы. В качестве простого примера приведем программу <code>awk</code>, в которой каждая входная строка заносится в отдельный элемент массива, индексируемого номером строки, а затем они печатаются в обратном порядке:</p>
    <p><code>$ cat backwards</code></p>
    <p><code># backwards: print input in backward line order</code></p>
    <p><code>awk ' { line[NR] = $0 }</code></p>
    <p><code>END   { for (i = NR; i &gt; 0; i--) print line[i] } ' $*</code></p>
    <p><code>$</code></p>
    <p>Заметьте, что подобно переменным, массивы не нужно описывать; размер массива ограничен только объемом памяти, доступным на вашей машине. Конечно, если очень большой файл заносится в массив, в конце концов, это может привести к исчерпанию ресурсов памяти. Для печати конца большого файла в обратном порядке следует обратиться за помощью к команде <code>tail</code>:</p>
    <p><code>$ tail -5 /usr/dict/web2 | backwards</code></p>
    <p><code>zymurgy</code></p>
    <p><code>zymotically</code></p>
    <p><code>zymotic</code></p>
    <p><code>zymosthenic</code></p>
    <p><code>zymosis</code></p>
    <p><code>$</code></p>
    <p>Команда <code>tail</code> использует возможности файловой системы — операцию "поиск" (seeking), позволяющую перейти к концу файла без чтения всей предшествующей информации. Подробнее эта операция будет рассмотрена при обсуждении функции <code>lseek</code> в гл. 7. (В нашей команде <code>tail</code> есть флаг <code>-r</code>, который определяет печать строк в обратном порядке, заменяя команду <code>backwards</code>).</p>
    <p>При обычной обработке входная строка разбивается на поля. Эту операцию можно выполнить с помощью встроенной функции <code>split</code> над любой строкой:</p>
    <p><code>n = split(s, arr, sep)</code></p>
    <p>Строка <code>s</code> разбивается на поля, записываемые в элементы массива <code>arr</code> от 1 до <code>n</code>. Используется символ разделения полей <code>sep</code>, если он задан; в противном случае применяется текущее значение переменной <code>FS</code>. Например, обращение <code>split($0, а, ":")</code> разбивает входную строку на столбцы, что подходит для обработки файла <code>/etc/passwd</code>, поэтому обращение <code>split("9/29/83", date, "/")</code> разбивает дату по символам дробной черты.</p>
    <p><code>$ sed 1q /etc/passwd | awk '{split($0, a, ":"); print a[1]}'</code></p>
    <p><code>root</code></p>
    <p><code>$ echo 9/29/83 | awk '{split($0, date, "/"); print date[3]}'</code></p>
    <p><code>83</code></p>
    <p><code>$</code></p>
    <p>В табл. 4.5 перечислены встроенные функции <code>awk</code>.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>cos(expr)</code></td>
      <td align="left" valign="top">Косинус <code>expr</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>exp(expr)</code></td>
      <td align="left" valign="top">Возведение в степень <code>expr</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>getline()</code></td>
      <td align="left" valign="top">Чтение следующей входной строки; возвращает 0 в случае конца файла, в противном случае 1</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>index(s1, s2)</code></td>
      <td align="left" valign="top">Положение строки <code>s2</code> в <code>s1</code>; возвращает 0, если строка не входит</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>int(expr)</code></td>
      <td align="left" valign="top">Целая часть <code>expr</code>; округляет по минимуму</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>length(s)</code></td>
      <td align="left" valign="top">Длина строки <code>s</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>log(expr)</code></td>
      <td align="left" valign="top">Натуральный логарифм <code>expr</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>sin(expr)</code></td>
      <td align="left" valign="top">Синус <code>expr</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>split(s, a, c)</code></td>
      <td align="left" valign="top">Разбиение <code>s</code> на <code>а[1]</code> ... <code>a[n]</code> по символу <code>c</code>; возвращает <code>n</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>sprintf(fmt, ...)</code></td>
      <td align="left" valign="top">Форматирование в соответствии со спецификацией <code>fmt</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>substr(s,m,n)</code></td>
      <td align="left" valign="top">Подстрока в <code>n</code> символов строки <code>s</code>, начинающаяся с индекса <code>m</code></td>
     </tr>
    </table>
    <p>Таблица 4.5: <strong>Встроенные функции <code>awk</code></strong></p>
    <empty-line/>
    <subtitle>Ассоциативные массивы</subtitle>
    <p>Стандартной задачей обработки данных является получение суммарных значений для множества пар имя значение. Иными словами, по входному потоку типа</p>
    <p><code>Susie 400</code></p>
    <p><code>John  100</code></p>
    <p><code>Mary  200</code></p>
    <p><code>Mary  300</code></p>
    <p><code>John  100</code></p>
    <p><code>Susie 100</code></p>
    <p><code>Mary  100</code></p>
    <p>мы хотим получить суммарные значения для каждого имени:</p>
    <p><code>John  200</code></p>
    <p><code>Mary  600</code></p>
    <p><code>Susie 500</code></p>
    <p>Программа <code>awk</code> предлагает изящное решение этой задачи — с помощью ассоциативных массивов. Хотя обычно мы представляем себе индекс массива как целое число, в <code>awk</code> любое значение можно использовать в качестве индекса. Поэтому</p>
    <p><code>    {sum[$1] += $2}</code></p>
    <p><code>END {for (name in sum) print name sum [name]}</code></p>
    <p>задает всю программу подсчета n печати сумм для пар имя значение независимо от порядка следования этих пар. Каждое имя (<code>$1</code>) служит индексом в массиве <code>sum</code>; в конце применена специальная форма цикла <code>for</code> для перебора всех элементов <code>sum</code> и их печати. Синтаксис этого варианта цикла <code>for</code> таков:</p>
    <p><code>for (перем in массив)</code></p>
    <p><code> оператор</code></p>
    <p>Хотя он может показаться вам искусственным, как цикл <code>for</code> языка <code>shell</code>, они никак не связаны. Цикл охватывает индексы массива, а не его элементы, устанавливая значение "перем" равным каждому индексу поочередно. Однако порядок появления индексов непредсказуем, поэтому может возникнуть необходимость в их сортировке. В приведенном примере выходной поток можно по конвейеру передать команде <code>sort</code>, чтобы имена шли в порядке убывания значений:</p>
    <p><code>$ awk '...' | sort +1nr</code></p>
    <p>Реализация ассоциативной памяти предполагает хэширование, чтобы доступ к одному элементу занимал столько же времени, сколько и к любому другому, и чтобы это время не зависело (по крайней мере для массивов средних размеров) от числа элементов в массиве.</p>
    <p>Использование ассоциативных массивов эффективно для вычислительных задач, таких, как подсчет частоты появления слов во входном потоке:</p>
    <p><code>$ cat wordfreq</code></p>
    <p><code>awk ' { for (i = 1; i &lt;= NF; i++) num[$i]++ }</code></p>
    <p><code>END   {for (word in num) print word, num[word] }</code></p>
    <p><code>' $*</code></p>
    <p><code>$ wordfreq ch4.* | sort +1 -nr | sed 20q | 4</code></p>
    <p><code>the 372 .CW 345 of  220 is   185</code></p>
    <p><code>to  175 a   167 in  109 and  100</code></p>
    <p><code>.PI  94 .P2  94 .PP  90 $     87</code></p>
    <p><code>awk  87 sed  83 that 76 for   75</code></p>
    <p><code>The  63 are  61 line 55 print 52</code></p>
    <p><code>$</code></p>
    <p>В первом цикле <code>for</code> выбирается каждое слово из входной строки и заполняется массив <code>num</code>, индексируемый словами. (Не путайте <code>$i</code>, обозначающее в <code>awk</code> i-е поле входной строки, с переменными языка <code>shell</code>.) После того как файл будет прочитан, во втором цикле <code>for</code> печатаются в произвольном порядке слова и частота их появления.</p>
    <cite>
     <subtitle>Упражнение 4.9</subtitle>
     <p>В результат действия команды <code>wordfreq</code> попали команды форматирования типа <code>.CW</code>, которые применяются для печати слов определенным шрифтом. Как избавиться от таких ненастоящих слов? Как бы вы использовали команду <code>tr</code>, чтобы программа <code>wordfreq</code> работала правильно, независимо от того, прописные или строчные буквы задействованы во входном потоке? Сравните реализацию и скорость выполнения программы <code>wordfreq</code>, конвейера из разд. 4.2 и предлагаемого ниже решения.</p>
     <p><code>sed 's/[&#8594;][&#8594;]*/\</code></p>
     <p><code>/q' $* | sort | uniq -c | sort -nr</code></p>
    </cite>
    <subtitle>Строки</subtitle>
    <p>Хотя обе команды, и <code>sed</code> и <code>awk</code>, предназначены для решения небольших задач типа выбора определенного поля, только <code>awk</code> используется в той степени, в какой предполагает настоящее программирование. Примером может служить программа, которая разбивает длинные строки, чтобы они занимали не более 80 позиций. Каждая строка, превышающая 80 символов, завершается после 80-го символа; в качестве предупреждения добавляется <code>\</code> и обрабатывается остаток строки. Хвост разбиваемой строки сдвигается к ее правому концу, а не к левому, что более удобно для программ печати, и именно поэтому мы обратимся к программе <code>fold</code>. Рассмотрим, в частности, строки из 20, а не из 80 позиций:</p>
    <p><code>$ cat тест</code></p>
    <p><code>Короткая строка</code></p>
    <p><code>Строка немного длиннее</code></p>
    <p><code>Эта строка еще длиннее, чем предыдущая строка</code></p>
    <p><code>$ fold тест</code></p>
    <p><code>Короткая строка</code></p>
    <p><code>Строка немного длиннее</code></p>
    <empty-line/>
    <p><code>Эта строка еще длиннее,</code></p>
    <p><code> чем предыдущая строка</code></p>
    <p><code>$</code></p>
    <p>Вам может показаться странным, что в седьмой версии системы нет программы для добавления или удаления символов табуляции, хотя команда <code>pr</code> в System V выполняет и то и другое. Наша реализация программы <code>fold</code> использует редактор <code>sed</code>, чтобы перевести символы табуляции в пробелы и чтобы счетчик числа символов в <code>awk</code> принял правильное значение. Это хороший способ при табуляции в начале строки (что типично для языковых программ), но номер позиции сбивается, если символ табуляции оказывается в середине строки:</p>
    <p><code># fold: fold long lines</code></p>
    <p><code>sed 's/\(-&gt;/ /g' $* |      # convert tabs to spaces</code></p>
    <p><code>awk '</code></p>
    <p><code> BEGIN {</code></p>
    <p><code>  N = 80                   # folds at column 80</code></p>
    <p><code>  for (i = 1; i &lt;= N; i++) # make a string of blanks</code></p>
    <p><code>   blanks = blanks " "</code></p>
    <p><code> }</code></p>
    <p><code> {</code></p>
    <p><code>  if ((n = length($0)) &lt;= N)</code></p>
    <p><code>   print</code></p>
    <p><code>  else {</code></p>
    <p><code>   for (i = 1; n &gt; N; n -= N) {</code></p>
    <p><code>    printf "%s\\\n", substr($0,i,N)</code></p>
    <p><code>    i += N;</code></p>
    <p><code>   }</code></p>
    <p><code>   printf "%s%s\n" , substr(blanks, 1, N-n), substr($0, I)</code></p>
    <p><code>  }</code></p>
    <p><code> } '</code></p>
    <p>На языке <code>awk</code> нет явной операции конкатенации строк; строки соединяются, если они следуют подряд. Вначале <code>blanks</code> является пустой строкой. Цикл в части <code>BEGIN</code> создает длинную строку пробелов конкатенацией: каждый шаг цикла прибавляет еще один пробел к концу строки <code>blanks</code>. Во втором цикле входная строка разбивается на части, пока оставшаяся часть не станет достаточно короткой. Как и в языке Си, операцию присваивания можно использовать в качестве выражения, поэтому в конструкции</p>
    <p><code>if ((n=length($0)) &lt;= N)...</code></p>
    <p>длина входной строки присваивается <code>n</code> до проверки значения. Обратите внимание на скобки.</p>
    <cite>
     <subtitle>Упражнение 4.10</subtitle>
     <p>Измените программу <code>fold</code> так, чтобы разрыв строки происходил на пробеле или символе табуляции, а не посреди слова. Сделайте эту программу пригодной и для длинных слов.</p>
    </cite>
    <subtitle>Взаимодействие с интерпретатором</subtitle>
    <p>Допустим, что вы намереваетесь написать программу <code>field n</code>. Эта программа будет печатать n-е поле каждой входной строки так, чтобы можно было, например, задать:</p>
    <p><code>$ who | field 1</code></p>
    <p>для печати только имен, под которыми пользователи входят в систему. Язык <code>awk</code> явно предоставляет возможность выбора полей. Наша основная задача — передать номер n программе <code>awk</code>. Ниже приведено одно из возможных решений:</p>
    <p><code>$ awk '{print $'$1'}'</code></p>
    <p>Здесь <code>$1</code> открыто (не внутри каких либо кавычек), и поэтому становится номером поля, доступным в программе <code>awk</code>. При ином решении используются кавычки:</p>
    <p><code>awk "{print \$$1}"</code></p>
    <p>Аргумент обрабатывается интерпретатором, поэтому <code>\$</code> становится <code>$</code>, а <code>$1</code> заменяется на значение <code>n</code>. Мы предпочитаем решение с апострофами (одиночными кавычками), поскольку при использовании кавычек в типичной программе <code>awk</code> появится слишком много символов <code>\</code>.</p>
    <p>Другим примером может служить программа <code>addup n</code>, суммирующая значения n-го поля:</p>
    <p><code>awk '{s += $'$1'}</code></p>
    <p><code>END {print s}'</code></p>
    <p>В третьем примере вычисляются отдельные суммы значений каждого n-го поля и полная сумма:</p>
    <p><code>awk '</code></p>
    <p><code>BEGIN { n = '$1' }</code></p>
    <p><code>{ for (i=1; i &lt;= n; i++)</code></p>
    <p><code>   sum[i] += $1</code></p>
    <p><code>}</code></p>
    <p><code>END { for(i = 1; i &lt;= n; i++)</code></p>
    <p><code>      {</code></p>
    <p><code>       printf "%6g ", sum[i]</code></p>
    <p><code>       total += sum[i]</code></p>
    <p><code>      }</code></p>
    <p><code>      printf "; total = %6g ", total</code></p>
    <p><code>    }'</code></p>
    <p>Нам удобнее было использовать часть <code>BEGIN</code> для засылки значения в переменную <code>n</code>, чем засорять конец программы кавычками.</p>
    <p>Основная трудность во всех приведенных выше примерах состоит не в том, чтобы следить за кавычками (хотя и это хлопотно), а в том, что программы, составленные показанным способом, могут читать только свой стандартный входной поток. Нет никакой возможности передать им сразу и параметр <code>n</code>, и произвольно длинный список имен файлов. Для этого требуется определенная техника программирования на языке <code>shell</code>; которую мы рассмотрим в следующей главе.</p>
    <subtitle>Служебная программа-календарь на языке <code>awk</code></subtitle>
    <p>В нашем последнем примере демонстрируются ассоциативные массивы, а также иллюстрируется взаимодействие с интерпретатором и частично показывается процесс разработки программы.</p>
    <p>Задача состоит в создании системы, посылающей вам каждое утро почту с напоминанием об ожидаемых событиях. (Возможно, такая календарная система уже есть; см. руководство по <code>calendar(1)</code>.) В этом разделе применяется иной подход. Вам будут перечислены события, происходящие сегодня и, кроме того, предстоящие сегодняшние и завтрашние события. Правильный учет праздников и выходных оставлен вам в качестве упражнения.</p>
    <p>Прежде всего нужно предусмотреть место, где будет храниться календарь. Имеет смысл разместить его в файле с именем <code>calendar</code> в каталоге <code>/usr/you</code>:</p>
    <p><code>$ cat calendar</code></p>
    <p><code>Sep 30 день рождения мамы</code></p>
    <p><code>Oct  1 обед с Джо, полдень</code></p>
    <p><code>Oct  1 встреча в 16:00</code></p>
    <p><code>$</code></p>
    <p>Далее, необходимо уметь просматривать календарь, отыскивая определенную дату. Существует масса вариантов; мы остановимся на языке <code>awk</code>, поскольку с его помощью легче выполнять арифметические операции по переходу от одной даты к другой, однако для этой цели подходят и другие программы, например <code>sed</code> и <code>egrep</code>. Конечно, строки, выбранные из файла <code>calendar</code>, посылаются командой <code>mail</code>.</p>
    <p>Наконец, вам придется научиться автоматически и безотказно просматривать календарь каждый день, скажем, рано утром. Это можно сделать с помощью команды <code>at</code>, о которой упоминалось в гл. 1.</p>
    <p>Если ограничить календарь таким форматом, в котором каждая строка начинается с названия месяца и числа (как это делает команда <code>date</code>), то составить первый вариант программы календаря нетрудно:</p>
    <p><code>$ date</code></p>
    <p><code>Thu Sep 29 15:23:12 EDT 1983</code></p>
    <p><code>$ cat bin/calendar</code></p>
    <p><code># calendar: version 1 - today only</code></p>
    <p><code>awk &lt;$HOME/calendar '</code></p>
    <p><code> BEGIN { split ("'"`date`"'", date) }</code></p>
    <p><code> $1 == date[2] &amp;&amp; $2 == date[3]</code></p>
    <p><code>' | mail $NAME</code></p>
    <p><code>$</code></p>
    <p>Функция в части <code>BEGIN</code> разбивает дату, выдаваемую командой <code>date</code>, и заносит ее в массив; второй и третий элементы массива — месяц и число. Мы предполагаем, что в переменной интерпретатора <code>NAME</code> находится имя, под которым вы вошли в систему. Вы заметили, какая нужна сложная последовательность кавычек, чтобы "поймать" результат действия команды <code>date</code> в середине строки программы <code>awk</code>. Более простым решением является передача даты в первой строке входного потока:</p>
    <p><code>$ cat /bin/calendar</code></p>
    <p><code># calendar: version 2 - today only, no quotes</code></p>
    <p><code>(date; cat $HOME/calendar) |</code></p>
    <p><code>awk '</code></p>
    <p><code> NR == 1 { mon = $2; day = $3 }   # set the date</code></p>
    <p><code> NR &gt; 1 &amp;&amp; $1 == mon &amp;&amp; $2 == day # print calendar lines</code></p>
    <p><code>' | mail $NAME</code></p>
    <p><code>$</code></p>
    <p>На следующем шаге требуется так изменить программу, чтобы искать сообщение с завтрашней датой так же, как и с сегодняшней. Наибольшие усилия затрачиваются на прибавление единицы к сегодняшнему числу. Но в конце месяца нужно перейти к следующему месяцу, а число приравнять единице. Конечно, число дней в разных месяцах различно. Именно здесь на помощь приходит ассоциативный массив. Два массива <code>days</code> и <code>nextmon</code>, индексами которых служат названия месяцев, содержат число дней месяца и название следующего месяца. Например, <code>days["Jan"]</code> равно 31, a <code>nextmon["Jan"]</code> есть <code>Feb</code>. Вместо того чтобы написать множество операторов типа</p>
    <p><code>days["Jan"] = 31; nextmon["Jan"] = "Feb"</code></p>
    <p><code>days["Feb"] = 28; nextmon["Feb"] = "Mar"</code></p>
    <p><code>...</code></p>
    <p>мы воспользуемся функцией <code>split</code> для преобразования удобно записываемой структуры данных в то, что требуется:</p>
    <p><code>$ cat calendar</code></p>
    <p><code># calendar: version 3 -- today and tomorrow</code></p>
    <p><code>awk &lt;$HOME/calendar '</code></p>
    <p><code>BEGIN {</code></p>
    <p><code> x = "Jan 31 Feb 28 Mar 31 Apr 30 May 31 Jun 30 " \</code></p>
    <p><code>     "Jul 31 Aug 31 Sep 30 Oct 31 Nov 30 Dec 31 Jan 31"</code></p>
    <p><code> split(x, data)</code></p>
    <p><code> for (i = 1; i &lt; 24; i += 2) {</code></p>
    <p><code>  days[data[i]] = data[i+1]</code></p>
    <p><code>  nextmon[data[i]] = data[i+2]</code></p>
    <p><code> }</code></p>
    <p><code> split("'"`date`", date)</code></p>
    <p><code> mon1 = date[2]; day1 = date[3]</code></p>
    <p><code> mon2 = mon1; day2 = day1 + 1</code></p>
    <p><code> if (day1 &gt;= days[mon1]) {</code></p>
    <p><code>  day2 = 1</code></p>
    <p><code>  mon2 = nextmon[mon1]</code></p>
    <p><code> }</code></p>
    <p><code>}</code></p>
    <p><code>$1 == mon1 &amp;&amp; $2 == day1 || $1 == mon2 &amp;&amp; $2 == day2</code></p>
    <p><code>' | mail $NAME</code></p>
    <p><code>$</code></p>
    <p>Обратите внимание на то, что <code>Jan</code> появляется дважды в структуре данных; такое "сторожевое" значение упрощает обработку для декабря.</p>
    <p>На последнем шаге нужно обеспечить запуск программы календаря на каждый день. Можно делать это и самому, не забывая задавать команду (каждый день!)</p>
    <p><code>$ at 5 am</code></p>
    <p><code>calendar</code></p>
    <p><code>ctl-d</code></p>
    <p><code>$</code></p>
    <p>Однако такое решение нельзя считать автоматическим или надежным. Хитрость заключается в том, чтобы не только запустить программу <code>calendar</code>, но и обеспечить следующий ее запуск.</p>
    <p><code>$ cat early.morning</code></p>
    <p><code>calendar</code></p>
    <p><code>echo early morning | at 5am</code></p>
    <p><code>$</code></p>
    <p>Вторая строка файла <code>early.morning</code> готовит еще одну команду <code>at</code> для следующего дня, поэтому, раз начавшись, эта последовательность команд сама себя воспроизводит. В команде <code>at</code> устанавливается <code>PATH</code>, текущий каталог и другие параметры запускаемых ею команд, так что больше ничего и не требуется.</p>
    <cite>
     <subtitle>Упражнение 4.11</subtitle>
     <p>Измените программу <code>calendar</code> так, чтобы она учитывала выходные дни: для пятницы "завтра" должно означать субботу, воскресенье или понедельник. Далее измените ее так, чтобы можно было учесть високосные годы. Следует ли учитывать праздники? Как бы вы это сделали?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 4.12</subtitle>
     <p>Должна ли программа календарь учитывать даты, находящиеся в середине строки, а не только в ее начале? Как быть с датой, заданной в другом формате, например 10/1/83?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 4.13</subtitle>
     <p>Почему в программе <code>calendar</code> используется <code>$NAME</code>, а не обращение к <code>getname</code>?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 4.14</subtitle>
     <p>Напишите вашу версию команды rm, которая не удаляет файлы, а пересылает их во временный каталог, используя команду <code>at</code> для очистки каталога в то время, пока вы не работаете.</p>
    </cite>
    <subtitle>Дополнительная информация</subtitle>
    <p>Язык <code>awk</code> довольно громоздкий, и в рамках одной главы трудно показать все его возможности. Поэтому мы перечислим здесь еще ряд моментов, на которые необходимо обратить внимание в справочном руководстве:</p>
    <p>• Переключение выходного потока оператора <code>print</code> в файлы и программные каналы: за каждым оператором <code>print</code> или <code>printf</code> может следовать символ <code>&gt;</code> и имя файла (в виде строки в кавычках или переменной); выходной поток будет направлен в этот файл. Как и для интерпретатора, <code>&gt;&gt;</code> означает добавление, а не запись. Для вывода в программный канал используется символ <code>|</code>, а не <code>&gt;</code>.</p>
    <p>• Запись в несколько строк: если разделитель записей <code>RS</code> установлен равным концу строки, то входные записи разделяются пустой строкой. В таком случае несколько входных строк могут рассматриваться как одна запись.</p>
    <p>• "Шаблон, шаблон" в качестве селектора: как и в случае команд <code>sed</code> и <code>ed</code>, с помощью пары шаблонов можно указать диапазон строк. Так выбираются строки, начиная с соответствующей первому шаблону, до строки, соответствующей второму шаблону. Приведем простой пример:</p>
    <p><code>NR == 10, NR == 20</code></p>
    <p>Здесь задаются строки от 10-й по 20-ю включительно.</p>
   </section>
   <section>
    <title>
     <p>4.5 Хорошие файлы и хорошие фильтры</p>
    </title>
    <p>Несмотря на то что в качестве примеров использования языка <code>awk</code> приводились независимые программы, в большинстве случаев его применяют для написания простых программ в одну или две строки, являющихся фильтрами в больших конвейерах. Это справедливо для большинства фильтров: редко поставленная задача может быть решена с помощью одного фильтра, чаще она разбивается на подзадачи, где фигурируют несколько фильтров, объединенных в конвейер. Такую реализацию программных компонентов называют основным принципом организации программного мира UNIX. Фильтры буквально "пронизывают" всю систему, и очень важно понимать причины этого.</p>
    <p>Программы UNIX выдают выходной поток в таком формате, что его можно использовать в качестве входного потока для других программ. Файлы, пропускаемые через фильтр, состоят из строк текста, свободных от декоративных заголовков, завершителей или пустых строк. Каждая строка представляет интерес — это имя файла, слово, описатель выполняемого процесса, поэтому программы типа <code>wc</code> или <code>grep</code> могут рассчитывать определенные характеристики объектов или искать их по именам. Если о каждом объекте имеется большая информация, файл все равно состоит из строк, разбиваемых на поля пробелами или символами табуляции, как в выводе команды <code>ls -l</code>. Располагая данными, разбитыми на такие поля, программы типа <code>awk</code> могут легко выбрать, обработать или переупорядочить информацию.</p>
    <p>Фильтры построены по общей схеме. Каждый из них пишет в стандартный выходной поток результат обработки файлов-аргументов или стандартного выходного потока, если аргументов нет. Аргументы задают входной поток и никогда не задают выходной<a l:href="#n11" type="note">[11]</a> , поэтому выходной поток команда всегда может передать в конвейер. Необязательные аргументы (или аргументы, не являющиеся файлами, такие, как шаблон в команде <code>grep</code>) задаются перед именем файлов. Наконец, сообщения об ошибках пишутся в стандартный поток диагностики, поэтому они не могут исчезнуть в конвейере.</p>
    <p>Эти соглашения не оказывают большого влияния на программы пользователя, но единообразное применение их ко всем программам обеспечивает простоту взаимодействия, что подтверждается многочисленными примерами на протяжении всей книги и наиболее наглядно продемонстрировано программой подсчета слов в конце разд. 4.2. Если каким-либо программам потребуется входной или выходной файл с конкретным именем, определенное обращение для спецификации параметров или создание заголовков и завершений, то схема конвейера работать не будет. И конечно, если бы система UNIX не предоставляла программные каналы, кому-то пришлось создать подобное стандартное средство. Однако программные каналы есть, и конвейеры работают. Их даже можно запрограммировать, но для этого вы должны знать возможности системы.</p>
    <cite>
     <subtitle>Упражнение 4.15</subtitle>
     <p>Команда <code>ps</code> выдает поясняющий заголовок, а команда <code>ls -l</code> сообщает общее число блоков файла. Прокомментируйте действие команд.</p>
    </cite>
    <cite>
     <subtitle>Историческая и библиографическая справка</subtitle>
     <p>Хороший обзор алгоритмов сопоставления шаблонов дается в статье Э. Ахо, создателя команды <code>egrep</code>, "Pattern matching in strings" (Proceedings of the Symposium on Formal Language Theory, Santa Barbara, 1979). Редактор <code>sed</code> разработан и реализован на базе редактора <code>ed</code> Л. Мак-Махоном. Язык <code>awk</code> был разработан и реализован Э. Ахо, П. Вайнбергером и Б. Керниганом, но это решение не очень элегантно. К тому же выбор названия языка по первым буквам имен создателей представляется не вполне удачным. Проект обсуждался в статье авторов "AWK — а pattern scanning and processing language" (Software-Practice and Experience, July, 1978). Язык <code>awk</code> имеет несколько источников, но, безусловно, некоторые идеи заимствованы из языка Снобол4, редактора <code>sed</code>, языка проверки условий, разработанного М. Рочкиндом, языковых средств <code>yacc</code> и <code>lex</code> и, конечно, языка Си. В действительности сходство между <code>awk</code> и Си порождает ряд проблем. Язык подобен Си, но они не совпадают: одни конструкции в <code>awk</code> отсутствуют, другие отличаются от соответствующих конструкций Си неочевидным образом.</p>
     <p>В статье Д. Комера "The flat file system FFG: a database system consisting of primitives". (Software — Practice and Experience, Nov., 1982) обсуждается использование интерпретатора и <code>awk</code> для создания системной базы данных.</p>
    </cite>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 5</p>
    <p>Программирование на языке <code>shell</code></p>
   </title>
   <section>
    <p>Большинство пользователей считают, что <code>shell</code> представляет собой диалоговый интерпретатор команд. На самом же деле это язык программирования, в котором каждый оператор инициирует запуск команды. Язык <code>shell</code> может показаться вам несколько странным, поскольку в нем находят отражение и диалоговый, и программный аспекты выполнения команд. Он формировался по плану, хотя и имеет свою историю. Разнообразие применений языка привело к некоторой незавершенности в деталях, но для его эффективного использования вам и не нужно разбираться во всех нюансах. В данной главе мы рассмотрим основы программирования с помощью <code>shell</code> на примерах разработки ряда полезных программ. При изучении материала желательно иметь под рукой страницу <code>sh(1)</code> справочного руководства по UNIX.</p>
    <p>Для интерпретатора, как и для многих других команд, особенности выполнения наиболее четко проявляются в ходе эксперимента. В справочном руководстве что-то может оказаться неясным, и здесь на помощь вам придет хороший пример. По этой причине материал главы построен на примерах, которые демонстрируют возможности языка. Мы будем обсуждать не только вопросы программирования с помощью интерпретатора, но и проблемы создания программ на языке <code>shell</code>, уделяя особое внимание тестированию и диалогу.</p>
    <p>Если вы написали программу на языке <code>shell</code> или каком-то ином языке, она может оказаться полезной и другим пользователям вашей системы. Однако требования, которым, по мнению других, должна удовлетворять программа, обычно оказываются более строгими, чем предъявляемые к ней вами. Поэтому важнейший аспект программирования на языке <code>shell</code> — обеспечение надежности программы, чтобы она могла выполняться даже при неверно заданных входных данных и выдавать полезную информацию об ошибках.</p>
   </section>
   <section>
    <title>
     <p>5.1 Совершенствование команды <code>cal</code></p>
    </title>
    <p>Типичная задача программирования на языке <code>shell</code> сводится к изменению взаимодействия между пользователем и программой, чтобы сделать это взаимодействие более удобным. В качестве примера рассмотрим команду <code>cal(1)</code>:</p>
    <p><code>$ cal</code></p>
    <p><code>usage: cal [month] year </code>Пока хорошо</p>
    <p><code>$ cal october</code></p>
    <p><code>Bad argument            </code>Уже не так хорошо</p>
    <p><code>$ cal 10 1983</code></p>
    <p><code>  October 1983</code></p>
    <p><code>  S  M  Tu W  Th F  S</code></p>
    <p><code>                    1</code></p>
    <p><code>  2  3  4  5  6  7  8</code></p>
    <p><code>  9 10 11 12 13 14 15</code></p>
    <p><code> 16 17 18 19 20 21 22</code></p>
    <p><code> 23 24 25 26 27 28 29</code></p>
    <p><code> 30 31</code></p>
    <p><code>$</code></p>
    <p>Досадно, что месяц нужно задавать числом, и к тому же, как оказалось, команда <code>cal 10</code> выдает календарь на весь 10-й год, а не на октябрь текущего года. Поэтому всегда следует указывать год, если вы хотите получить календарь на один месяц.</p>
    <p>Указанные выше неудобства связаны с тем, что взаимодействие пользователя с программой было реализовано без привлечения команды <code>cal</code>. Вы можете изменить характер этого взаимодействия, не меняя самой программы. Если поместить команду в ваш собственный каталог <code>bin</code>, то возможен более удобный способ перевода аргументов в те, которые нужны настоящей команде <code>cal</code>. Вы можете даже вызывать свою версию команды, и тогда вам меньше придется запоминать.</p>
    <p>Первый шаг разработки — определить функции усовершенствованной команды <code>cal</code>. В основном мы хотим от нее разумного поведения. Месяц нужно распознавать по названию. При наличии двух аргументов она должна делать то же, что делала прежняя версия, за исключением перевода названия месяца в его номер. В случае одного аргумента следует печатать календарь месяца или года (в зависимости от того, что вам требуется), а при отсутствии аргументов — календарь текущего месяца, так как большей частью именно для этого и обращаются к команде. Поэтому задача сводится к тому, чтобы определить, сколько аргументов задано, и преобразовать их в те параметры, которые требуются стандартной команде <code>cal</code>.</p>
    <p>Язык <code>shell</code> имеет оператор <code>case</code>, который успешно применяется в таких ситуациях:</p>
    <p><code>case слово in</code></p>
    <p><code>шаблон) команды ;;</code></p>
    <p><code>шаблон) команды ;;</code></p>
    <p><code>...</code></p>
    <p><code>esac</code></p>
    <p>В операторе <code>case</code> <emphasis>слово</emphasis> сравнивается поочередно со всеми шаблонами от начала до конца и выполняются <emphasis>команды</emphasis>, связанные с первым (и только первым) <emphasis>шаблоном</emphasis>, соответствующим <emphasis>слову</emphasis>. Шаблоны составляются по правилам соответствия шаблонов, которые в некоторой степени обобщают правила задания имен файлов. Каждое действие завершается двумя символами <code>;;</code> (для последнего варианта можно обойтись без <code>;;</code>, но обычно мы ставим их для удобства редактирования).</p>
    <p>В нашей версии команды определяется число заданных аргументов, обрабатываются названия месяцев, затем происходит обращение к настоящей команде <code>cal</code>. В переменной интерпретатора <code>$#</code> хранится число аргументов, с которыми была вызвана программа; другие специальные переменные интерпретатора перечислены в табл. 5.1.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>$#</code></td>
      <td align="left" valign="top">Число аргументов</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$*</code></td>
      <td align="left" valign="top">Все аргументы, передаваемые интерпретатору</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$@</code></td>
      <td align="left" valign="top">Аналогично <code>$*</code>; см. разд. 5.7</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$-</code></td>
      <td align="left" valign="top">Флаги, передаваемые интерпретатору</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$?</code></td>
      <td align="left" valign="top">Возвращение значения последней выполненной команды</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$$</code></td>
      <td align="left" valign="top">Номер процесса интерпретатора</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$!</code></td>
      <td align="left" valign="top">Номер процесса последней команды, запущенной с помощью <code>&amp;</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$НOМЕ</code></td>
      <td align="left" valign="top">Аргумент, принятый по умолчанию для команды <code>cd</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$IFS</code></td>
      <td align="left" valign="top">Список символов, разделяющих слова в аргументах</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$MAIL</code></td>
      <td align="left" valign="top">Файл, изменение которого приводит к появлению сообщения "you have a mail" ("У вас есть почта")</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$PATH</code></td>
      <td align="left" valign="top">Список каталогов, в которых осуществляется поиск команд</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$PS1</code></td>
      <td align="left" valign="top">Строка приглашение, по умолчанию принята <code>'$'</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>$PS2</code></td>
      <td align="left" valign="top">Строка приглашение при продолжении командной строки, по умолчанию принята <code>'&gt;'</code></td>
     </tr>
    </table>
    <p>Таблица 5.1: <strong>Встроенные переменные интерпретатора</strong></p>
    <empty-line/>
    <p><code>$ cat cal</code></p>
    <empty-line/>
    <p><code># cal: nicer interface to /usr/bin/cal</code></p>
    <empty-line/>
    <p><code>case $# in</code></p>
    <p><code>0) set `date`; m=$2; y=$6 ;; # no args: use today</code></p>
    <p><code>1) m=$l; set `date`; y=$6 ;; #1 arg: use this year</code></p>
    <p><code>*) m=$1; y=$2 ;;             #2 args: month and year</code></p>
    <p><code>esac</code></p>
    <empty-line/>
    <p><code>case $m in</code></p>
    <p><code>jan*|Jan*) m=1 ;;</code></p>
    <p><code>feb*|Feb*) m=2 ;;</code></p>
    <p><code>mar*|Mar*) m=3 ;;</code></p>
    <p><code>apr*|Apr*) m=4 ;;</code></p>
    <p><code>may*|May*) m=5 ;;</code></p>
    <p><code>jun*|Jun*) m=6 ;;</code></p>
    <p><code>jul*|Jul*) m=7 ;;</code></p>
    <p><code>aug*|Aug*) m=8 ;;</code></p>
    <p><code>sep*|Sep*) m=9 ;;</code></p>
    <p><code>oct*|Oct*) m=10 ;;</code></p>
    <p><code>nov*|Nov*) m=11 ;;</code></p>
    <p><code>dec*|Dec*) m=12 ;;</code></p>
    <p><code>[1-9]|10|11|12) ;; # numeric month</code></p>
    <p><code>*) y=$m; m="" ;;   # plain year</code></p>
    <p><code>esac</code></p>
    <empty-line/>
    <p><code>/usr/bin/cal $m $y # run the real one</code></p>
    <p><code>$</code></p>
    <p>В первом операторе case проверяется число аргументов <code>$#</code> и выбирается подходящее действие. Последний шаблон в этом операторе задает вариант, выбираемый по умолчанию; если число аргументов не 0 и не 1, будет выполнен последний вариант. (Поскольку шаблоны просматриваются по порядку, вариант по умолчанию должен быть последним.) При наличии двух аргументов <code>m</code> и <code>y</code> принимают значение месяца и года, и наша команда <code>cal</code> должна выполняться как исходная команда.</p>
    <p>Первый оператор <code>case</code> включает пару нетривиальных строк, содержащих</p>
    <p><code>set `date`</code></p>
    <p>Хотя это сразу и не очевидно, легко установить действие команды, запустив ее:</p>
    <p><code>$ date</code></p>
    <p><code>Sat Oct 1 06:05:18 EDT 1983</code></p>
    <p><code>$ set `date`</code></p>
    <p><code>$ echo $1</code></p>
    <p><code>Sat</code></p>
    <p><code>$ echo $4</code></p>
    <p><code>06:05:20</code></p>
    <p><code>$</code></p>
    <p>Итак, мы имеем дело с встроенной командой интерпретатора, возможности которой многообразны. При отсутствии аргументов она выдает, как указывалось в гл. 3, значения переменных окружения. В случае обычных аргументов переопределяются значения <code>$1</code>, <code>$2</code> и т.д. Поэтому <code>set `date`</code> присваивает <code>$1</code> — день недели, <code>$2</code> — название месяца и т.д. Таким образом, при отсутствии аргументов в первом <code>case</code> месяц и год устанавливаются из текущей даты. Если был задан один аргумент, он используется в качестве месяца, а год берется из текущей даты.</p>
    <p>Команда <code>set</code> имеет также несколько флагов, из которых наиболее часто используются флаги <code>-v</code> и <code>-х</code> — для отключения эха команд при обработке их интерпретатором. Такое отключение может оказаться необходимым в процессе отладки сложных программ на языке <code>shell</code>.</p>
    <p>Теперь осталось только перевести значение месяца, если оно представлено в строковом виде, в число. Это делается с помощью второго оператора <code>case</code>, который практически очевиден. Единственный нюанс состоит в том, что символ <code>|</code> в шаблонах оператора <code>case</code>, как и в команде <code>egrep</code>, означает альтернативу: <code>малый|большой</code> соответствует варианту "малый" или "большой". Конечно, эти варианты можно было бы задать с помощью <code>[jJ]an*</code> и т.д. Программа допускает задание месяца строчными буквами, поскольку большинство команд работает с входным потоком, где данные записаны строчными буквами (иногда первая буква — прописная), поскольку так выглядит вывод команды <code>date</code>. Правила сопоставления шаблонов приведены в табл. 5.2.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>*</code></td>
      <td align="left" valign="top">Задает любую строку, включая пустую</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>?</code></td>
      <td align="left" valign="top">Задает любой одиночный символ</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>[ccc]</code></td>
      <td align="left" valign="top">Задает любой из символов в <code>ccc [a-d0-3]</code> эквивалентно <code>[abcd0123]</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>"..."</code></td>
      <td align="left" valign="top">Задает в точности <code>...</code>; кавычки защищают от специальных символов. Аналогично действует <code>'...'</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\c</code></td>
      <td align="left" valign="top">Задает <code>с</code> буквально</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>a|b</code></td>
      <td align="left" valign="top">Только для выражений выбора; задает <code>а</code> или <code>b</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>/</code></td>
      <td align="left" valign="top">Для имен файлов; соответствует только символу <code>/</code> в выражении; для выражений выбора сопоставляется, как любой другой символ</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.</code></td>
      <td align="left" valign="top">Если это первый символ в имени файла, то сопоставляется только с явно заданной точкой в выражении</td>
     </tr>
    </table>
    <p>Таблица 5.2: <strong>Правила сопоставления шаблонов в интерпретаторе</strong></p>
    <empty-line/>
    <p>Два последних варианта второго оператора <code>case</code> относятся к случаю, когда единственный аргумент может быть годом; напомним, что в первом операторе <code>case</code> предполагалось, что аргументом является месяц. Если это число, которым может быть задан месяц, то ничего не происходит (иначе предполагается, что задан год).</p>
    <p>Наконец, в последней строке вызывается <code>/usr/bin/cal</code> (настоящая команда <code>cal</code>) с преобразованными аргументами. Наша версия команды <code>cal</code> работает так, как этого мог бы ожидать начинающий:</p>
    <p><code>$ date</code></p>
    <p><code>Sat Oct 1 06:09:55 EDT 1983</code></p>
    <p><code>$ cal</code></p>
    <p><code>October 1983</code></p>
    <p><code>  S  М Tu  W Th  F  S</code></p>
    <p><code>                    1</code></p>
    <p><code>  2  3  4  5  6  7  8</code></p>
    <p><code>  9 10 11 12 13 14 15</code></p>
    <p><code> 16 17 18 19 20 21 22</code></p>
    <p><code> 23 24 25 26 27 28 29</code></p>
    <p><code> 30 31</code></p>
    <p><code>$ cal dec</code></p>
    <p><code>December 1983</code></p>
    <p><code>  S  M Tu  W Th  F  S</code></p>
    <p><code>              1  2  3</code></p>
    <p><code>  4  5  6  7  8  9 10</code></p>
    <p><code> 11 12 13 14 15 16 17</code></p>
    <p><code> 18 19 20 21 22 23 24</code></p>
    <p><code> 25 26 27 28 29 30 31</code></p>
    <p><code>$</code></p>
    <p>При обращении к <code>cal 1984</code> будет напечатан календарь на весь 1984 год. Наша обобщенная команда <code>cal</code> выполняет то же задание, что и исходная, но более простым и легко запоминающимся способом. Поэтому мы предпочитаем называть ее <code>cal</code>, а не <code>calendar</code> (что уже является командой), или как-нибудь еще с менее простой мнемоникой, например <code>ncal</code>. При использовании одного и того же имени пользователю не придется вырабатывать новые рефлексы для печати календаря.</p>
    <p>Прежде чем завершить обсуждение оператора <code>case</code>, следует объяснить, почему правила сопоставления шаблонов в интерпретаторе отличаются от правил для редактора <code>ed</code> и его производных. Действительно, наличие двух видов шаблонов означает, что нужно изучать два набора правил и иметь два программных фрагмента для их обработки. Некоторые различия вызваны просто неудачным выбором, который никогда не был зафиксирован. В частности, нет никаких причин (кроме того, что так сложилось исторически), по которым <code>ed</code> использует <code>'.'</code> а интерпретатор — <code>'?'</code> для задания единственного символа. Но иногда шаблоны применяются по-разному. Регулярные выражения в редакторе используются для поиска последовательности символов, которая может встретиться в любом месте строки; специальные символы и $ нужны, чтобы направить поиск с начала или конца строки. Но для имен файлов мы хотим, чтобы направление поиска определялось по умолчанию, поскольку это наиболее общий случай. Было бы неудобным задавать нечто вроде</p>
    <p><code>$ ls ^?*.с$</code> Так не получится</p>
    <p>вместо</p>
    <p><code>$ ls *.с</code></p>
    <cite>
     <subtitle>Упражнение 5.1</subtitle>
     <p>Если пользователи предпочтут вашу версию команды <code>cal</code>, как бы вы сделали ее общедоступной? Что следует предпринять, чтобы поместить ее в <code>/usr/bin</code>?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.2</subtitle>
     <p>Имеет ли смысл сделать так, чтобы при обращении <code>cal 83</code> был напечатан календарь за 1983 г.? Как в этом случае задать вывод календаря?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.3</subtitle>
     <p>Модифицируйте команду <code>cal</code> так, чтобы можно было задавать больше одного месяца, например:</p>
     <p><code>$ cal oct nov</code></p>
     <p>и даже диапазон месяцев:</p>
     <p><code>$ cal oct-dec</code></p>
     <p>Если сейчас декабрь, а вы выполняете обращение <code>cal jan</code>, то какой должен быть напечатан календарь: на январь этого года или следующего? Когда следует прекратить расширять возможности команды <code>cal</code>?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>5.2 Что представляет собой команда <code>which</code>?</p>
    </title>
    <p>При обзаведении собственными версиями команд, аналогичных <code>cal</code>, возникает ряд трудностей. В частности, когда вы работаете как пользователь Мэри и вошли в систему под именем <code>mary</code>, то, вводя команду <code>cal</code>, получаете стандартную версию команды вместо новой, если, конечно, не установили в своем каталоге <code>bin</code> связь с новой командой <code>cal</code>. Это может привести к путанице: вспомните, что сообщения об ошибках в исходной, команде <code>cal</code> не очень вразумительны. Мы привели всего лишь один пример возникающих трудностей. Поскольку интерпретатор осуществляет поиск команд среди каталогов, задаваемых переменной <code>PATH</code>, всегда есть вероятность столкнуться не с той версией команды, которую вы ожидали. Например, если вы задали команду, скажем <code>echo</code>, имя выполняемого на самом деле файла будет <code>./echo</code>, <code>/bin/echo</code>, <code>/usr/bin/echo</code> или какое-то другое в зависимости от компонентов вашей переменной <code>PATH</code> и от того, где находятся файлы. Может случиться, что в вашей последовательности поиска ранее, чем вы ожидали, окажется выполняемый файл с правильным именем, но не с теми результатами. Наиболее типичным примером в такой ситуации является команда <code>test</code>, которую мы обсудим позднее. Ее название настолько распространено для временной версии программы, что вызовы "не тех" команд <code>test</code> происходят раздражающе часто<a l:href="#n12" type="note">[12]</a>. Здесь весьма полезным средством явилась бы команда, которая помогла бы выяснить, какая версия программы должна выполняться.</p>
    <p>Один из вариантов решения — цикл поиска по каталогам, указанным в <code>PATH</code>, выполняемого файла с данным именем. В гл. 3 мы использовали цикл <code>for</code> по именам файлов и аргументам. Здесь же нужен такой цикл:</p>
    <p><code>for i in компонента в PATH</code></p>
    <p><code>do</code></p>
    <p><code> если заданное имя в каталоге i</code></p>
    <p><code>  печать полного путевого имени</code></p>
    <p><code>done</code></p>
    <p>Поскольку любую команду можно запустить внутри символов слабого ударения очевидное решение состоит в том, чтобы запустить <code>sed</code> по значению <code>PATH</code>, заменив двоеточия на пробелы. Мы можем проверить это с помощью нашего старого друга <code>echo</code>:</p>
    <p><code>$ echo $PATH</code></p>
    <p><code>:/usr/you/bin:/bin:/usr/bin </code>4 компонента</p>
    <p><code>$ echo $PATH | sed 's/:/ /a'</code></p>
    <p><code>/usr/you/bin /bin /usr/bin  </code>Только три выдано!</p>
    <p><code>$ echo `echo $PATH | sed 's/:/ /g'`</code></p>
    <p><code>/usr/you/bin /bin /usr/bin  </code>По-прежнему только три</p>
    <p><code>$</code></p>
    <p>Однако такое решение проблематично. Пустая строка в <code>PATH</code> служит синонимом <code>'.'</code>. Поэтому перевод двоеточий в пробелы не слишком удачен — теряется информация о пустых компонентах. Для создания правильного списка каталогов пустую компоненту <code>PATH</code> нужно перевести в точку. Пустая компонента может быть в середине, начале или конце строки, так что вам придется потрудиться, чтобы учесть все возможные случаи:</p>
    <p><code>$ echo $PATH | sed 's/^:/./</code></p>
    <p><code>&gt;                   s/::/:.:/g</code></p>
    <p><code>&gt;                   s/:$/:./</code></p>
    <p><code>&gt;                   s/:/ /g'</code></p>
    <p><code>. /usr/you/bin /bin /usr/bin</code></p>
    <p><code>$</code></p>
    <p>Мы могли бы записать это с помощью четырех отдельных команд <code>sed</code>, но так как редактор <code>sed</code> производит замены по порядку, можно выполнить все операции за один вызов.</p>
    <p>После задания каталогов в компонентах <code>PATH</code> упомянутая выше команда <code>test(1)</code> может вывести сообщение о том, существует ли файл в каждом каталоге. В принципе команда <code>test</code> — одна из самых "неуклюжих" программ UNIX. Например, команда <code>"test -r файл"</code> проверяет, существует ли файл и можно ли его читать; <code>"test -w файл"</code> проверяет, существует ли файл и можно ли в него писать, но в седьмой версии нет команды <code>test -х</code> (хотя в System V и других версиях есть), а именно она нам и нужна. Мы примем, что обращение <code>"test -f файл"</code> будет проверять, существует ли файл и не является ли он каталогом, т.е. представляет ли он собой обычный файл. Но вам следует обратиться к соответствующей странице справочного руководства, поскольку имеет хождение несколько версий.</p>
    <p>Каждая команда вырабатывает <emphasis>код завершения</emphasis> — значение, передаваемое интерпретатору и показывающее, что произошло. Это небольшое целое число, которое устанавливается по соглашению. Так, нуль может означать "истину" (команда успешно завершена), а ненулевое значение трактуется как "ложь" (выполнение команды было неудачным). Обратите внимание на то, что выбранные здесь значения противоположны значениям истины и лжи в языке Си.</p>
    <p>Поскольку ложь может представлять множество различных значений, причина неудачи обозначается кодом завершения по лжи. Например, команда grep возвращает 0, если произошло сопоставление, 1 — если сопоставления не было, и 2 — в случае ошибки в шаблоне или именах файлов. Каждая программа возвращает код завершения, хотя обычно нас не интересует его значение. Команда <code>test</code> неординарна: ее единственное назначение состоит в передаче кода завершения. Она ничего не выводит и не изменяет файлы.</p>
    <p>Интерпретатор хранит код завершения последней программы в переменной <code>$?</code>:</p>
    <p><code>$ cmp /usr/you/.profile /usr/you/.profile</code></p>
    <p><code>$ </code>Выдачи нет, файлы совпадают</p>
    <p><code>$ echo $?</code></p>
    <p><code>0 </code>0 означает успех, файлы идентичны</p>
    <p><code>$ cmp /usr/you/.profile /usr/mary/.profile</code></p>
    <p><code>/usr/you/.profile /usr/mary/.profile differ: char 6, line 3</code></p>
    <p><code>$ echo $?</code></p>
    <p><code>1 </code>He нуль означает, что файлы различны</p>
    <p><code>$</code></p>
    <p>У некоторых команд, таких, как <code>cmp</code> и <code>grep</code>, есть флаг <code>-s</code>, который заставляет их завершить выполнение с определенным кодом, но подавляет вывод. Оператор <code>if</code> языка <code>shell</code> запускает команды в зависимости от кода завершения некоторой команды, а именно:</p>
    <p><code>if команда</code></p>
    <p><code>then</code></p>
    <p><code> команды, если условие верно</code></p>
    <p><code>else</code></p>
    <p><code> команды, если условие ложно</code></p>
    <p><code>fi</code></p>
    <p>Местоположение символов перевода строк очень важно: <code>fi</code>, <code>then</code> и <code>else</code> распознаются только после символа перевода строки или точки с запятой.</p>
    <p>Оператор <code>if</code> всегда запускает команду (условие), тогда как в операторе <code>case</code> сопоставление с шаблоном производится самим интерпретатором. В некоторых версиях UNIX, включая System V, <code>test</code> является встроенной командой интерпретатора, поэтому <code>if</code> и <code>test</code> будут выполняться так же быстро, как и <code>case</code>. Если <code>test</code> — не встроенная команда, то операторы <code>case</code> более эффективны, чем операторы <code>if</code>, и следует использовать именно их для поиска шаблонов;</p>
    <p><code>$ case "$1" in</code></p>
    <p><code>hello) command</code></p>
    <p><code>esac</code></p>
    <p>выполняется быстрее, чем</p>
    <p><code>if test "$1"==hello</code> Медленнее, если test не встроенная</p>
    <p><code>then</code></p>
    <p><code> command</code></p>
    <p><code>fi</code></p>
    <p>Это одна из причин, по которой в языке <code>shell</code> иногда для проверки условий применяются операторы <code>case</code>, хотя в большинстве языков программирования использовались бы операторы <code>if</code>. С другой стороны, с помощью оператора <code>case</code> непросто определить, имеется ли право доступа к файлу на чтение; здесь предпочтение следует отдать команде <code>test</code> и оператору <code>if</code>.</p>
    <p>Итак, теперь мы готовы воспользоваться первой версией команды <code>which</code>, которая выведет сообщение о том, какой файл соответствует команде:</p>
    <p><code>$ cat which</code></p>
    <p><code># which cmd: which cmd in PATH is executed, version 1</code></p>
    <empty-line/>
    <p><code>case $# in</code></p>
    <p><code>0) echo 'Usage: which command' 1&gt;&amp;2; exit 2</code></p>
    <p><code>esac</code></p>
    <p><code>for i in `echo $PATH | sed 's/^:/.:/</code></p>
    <p><code>                            s/::/:.:/g</code></p>
    <p><code>                            s/:$/:./</code></p>
    <p><code>                            s/:/ /g'`</code></p>
    <p><code>do</code></p>
    <p><code> if test -f $i/$1 # use test -x if you can</code></p>
    <p><code> then</code></p>
    <p><code>  echo $i/$1</code></p>
    <p><code>  exit 0 # found it</code></p>
    <p><code> fi</code></p>
    <p><code>done</code></p>
    <p><code>exit 1   # not found</code></p>
    <p><code>$</code></p>
    <p>Проверим ее:</p>
    <p><code>$ cx which</code> Сделаем ее выполняемой</p>
    <p><code>$ which which</code></p>
    <p><code>./which</code></p>
    <p><code>$ which ed</code></p>
    <p><code>/bin/ed</code></p>
    <p><code>$ mv which /usr/you/bin</code></p>
    <p><code>$ which which</code></p>
    <p><code>/usr/you/bin/which</code></p>
    <p><code>$</code></p>
    <p>Первый оператор <code>case</code> осуществляет контроль ошибки. Обратите внимание на переключение <code>1&gt;&amp;2</code> в команде <code>echo</code>, которое выполняется для того, чтобы сообщение об ошибке не пропало в программном канале. Встроенная команда интерпретатора <code>exit</code> может использоваться для передачи кода завершения. В нашем примере <code>exit 2</code> передает код завершения в ситуации, когда команда не выполняется, <code>exit 1</code> — в ситуации, когда файл не удалось найти, и <code>exit 0</code> — в ситуации, когда файл найден. Если нет явного оператора <code>exit</code>, кодом завершения командного файла является код завершения последней выполняемой команды.</p>
    <p>Что произойдет, если в вашем текущем каталоге есть программа под именем <code>test</code>? (Мы предполагаем, что <code>test</code> не является встроенной командой.)</p>
    <p><code>$ echo 'echo hello' &gt;test </code>Сделаем поддельную команду test</p>
    <p><code>$ cx test                 </code>Сделаем ее выполняемой</p>
    <p><code>$ which which             </code>Попробуем which теперь</p>
    <p><code>hello                     </code>Неудача!</p>
    <p><code>./which</code></p>
    <p><code>$</code></p>
    <p>Вывод: требуется больший контроль. Можно запустить команду <code>which</code> (если нет команды <code>test</code> в текущем каталоге), чтобы определить полное имя для <code>test</code> и задать его явно. Но это не лучшее решение, поскольку команда <code>test</code> может присутствовать в различных каталогах в разных версиях системы, а команда <code>which</code> зависит от <code>sed</code> и <code>echo</code>, так что необходимо указать и их полные имена. Можно поступить проще — установить значение <code>PATH</code> в командном файле так, чтобы поиск команд осуществлялся только в <code>/bin</code> и <code>/usr/bin</code>. Конечно, это возможно только в команде <code>which</code>, причем прежнее значение <code>PATH</code> следует сохранить для определения последовательности каталогов при поиске.</p>
    <p><code>$ cat which</code></p>
    <p><code># which cmd: which cmd in PATH is executed, final version</code></p>
    <empty-line/>
    <p><code>opath=$PATH</code></p>
    <p><code>PATH=/bin:/usr/bin</code></p>
    <empty-line/>
    <p><code>case $# in</code></p>
    <p><code>0) echo 'Usage: which command' 1&gt;&amp;2; exit 2</code></p>
    <p><code>esac</code></p>
    <p><code>for i in `echo $opath | sed 's/^:/.:/</code></p>
    <p><code>                             s/::/:.:/g</code></p>
    <p><code>                             s/ :$/:./</code></p>
    <p><code>                             s/:/ /g'`</code></p>
    <p><code>do</code></p>
    <p><code> if test -f $i/$1 # this is /bin/test</code></p>
    <p><code> then # or /usr/bin/test only</code></p>
    <p><code>  echo $i/$1</code></p>
    <p><code>  exit 0 # found it</code></p>
    <p><code> fi</code></p>
    <p><code>done</code></p>
    <p><code>exit 1   # not found</code></p>
    <p><code>$</code></p>
    <p>Теперь команда <code>which</code> выполняется даже в том случае, если есть "поддельная" команда <code>test</code> (<code>sed</code> или <code>echo</code>) среди каталогов, входящих в <code>PATH</code>.</p>
    <p><code>$ ls -l test</code></p>
    <p><code>-rwxrwxrwx 1 you 11 Oct 1 06:55 test</code> Все еще здесь</p>
    <p><code>$ which which</code></p>
    <p><code>/usr/you/bin/which</code></p>
    <p><code>$ which test</code></p>
    <p><code>./test</code></p>
    <p><code>$ rm test</code></p>
    <p><code>$ which test</code></p>
    <p><code>/bin/test</code></p>
    <p><code>$</code></p>
    <p>В языке <code>shell</code> имеются две операции, объединяющие команды <code>||</code> и <code>&amp;&amp;</code>, использование которых часто более компактно и удобно, чем оператора <code>if</code>. Например, операция <code>||</code> может заменить некоторые операторы <code>if</code>:</p>
    <p><code>test -f имя_файла || echo имя_файла не существует</code></p>
    <p>эквивалентно</p>
    <p><code>if test ! -f имя_файла</code> ! обращает условие</p>
    <p><code>then</code></p>
    <p><code> echo имя файла не существует</code></p>
    <p><code>fi</code></p>
    <p>Операция <code>||</code>, несмотря на свой вид, не имеет ничего общего с конвейерами — это обычная операция, означающая ИЛИ. Выполняется команда слева от <code>||</code>. Если ее код завершения 0 (успех), справа от <code>||</code> команда игнорируется. Если команда слева возвращает другое значение (неудача), выполняется команда справа, и значение всего выражения есть код завершения правой команды. Иными словами, <code>||</code> представляет собой обычную операцию ИЛИ, которая не выполняет свою правую часть, если левая часть завершилась успешно. Соответственно <code>&amp;&amp;</code> есть обычная операция И, выполняющая свою правую часть, только если левая часть завершилась успешно.</p>
    <cite>
     <subtitle>Упражнение 5.4</subtitle>
     <p>Почему в команде <code>which</code> перед выходом из нее не восстанавливается значение <code>PATH</code> из <code>opath</code>?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.5</subtitle>
     <p>Если в языке <code>shell</code> используется <code>esac</code> для завершения оператора <code>case</code> и <code>fi</code> для завершения оператора <code>if</code>, почему для завершения оператора <code>do</code> применяется <code>done</code>?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.6</subtitle>
     <p>Введите в команду <code>which</code> флаг <code>-а</code>, чтобы выводились все файлы из <code>PATH</code>, а не только первый найденный.</p>
     <p><emphasis>Подсказка</emphasis>: <code>match='exit 0'</code></p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.7</subtitle>
     <p>Модифицируйте команду <code>which</code> так, чтобы она учитывала встроенные в язык <code>shell</code> команды типа <code>exit</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.8</subtitle>
     <p>Модифицируйте команду <code>which</code> так, чтобы она проверяла права доступа файлов. Как изменить ее для получения диагностического сообщения, если файл не удалось найти?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>5.3 Циклы <code>while</code> и <code>until</code>: контроль входа в систему</p>
    </title>
    <p>В гл. 3 цикл <code>for</code> использовался для нескольких итеративных программ. Обычно цикл <code>for</code> охватывает множество имен файлов, как в <code>'for i in * .с'</code>, или все аргументы командного файла, как в <code>'for i in $*'</code>. Но циклы в языке <code>shell</code> могут быть более общими, чем в этих идиомах, например цикл <code>for</code> в команде <code>which</code>.</p>
    <p>Имеются три вида циклов: <code>for</code>, <code>while</code> и <code>until</code>. Чаще всего используется цикл <code>for</code>. В нем выполняется последовательность команд (тело цикла) для каждого элемента из множества слов. В большинстве случаев множество образуют просто имена файлов. В циклах <code>while</code> и <code>until</code> контроль над выполнением тела цикла осуществляется с помощью кода завершения команды. Тело цикла выполняется до тех пор, пока команда условия не вернет ненулевой код для <code>while</code> или нуль для <code>until</code>. Циклы <code>while</code> и <code>until</code> идентичны, за исключением кода завершения команды.</p>
    <p>Ниже приведены основные формы каждого цикла:</p>
    <p><code>for i in список слов</code></p>
    <p><code>do</code></p>
    <p><code> тело цикла, $i последовательно получает значения элементов</code></p>
    <p><code>done</code></p>
    <empty-line/>
    <p><code>for i (явно перечисляются аргументы командного файла, т.е. $*)</code></p>
    <p><code>do</code></p>
    <p><code> тело цикла, $i последовательно получает значения аргументов</code></p>
    <p><code>done</code></p>
    <empty-line/>
    <p><code>while команда</code></p>
    <p><code>do</code></p>
    <p><code> тело цикла выполняется, пока команда возвращает истина</code></p>
    <p><code>done</code></p>
    <empty-line/>
    <p><code>until команда</code></p>
    <p><code>do</code></p>
    <p><code> тело цикла выполняется, пока команда возвращает ложь</code></p>
    <p><code>done</code></p>
    <p>Вторая форма цикла <code>for</code>, в которой пустой список обозначается как <code>$*</code>, является удобным сокращением записи для наиболее типичного использования.</p>
    <p>Командой условия, управляющей циклами <code>while</code> или <code>until</code>, может быть любая команда. Очевидным примером служит цикл <code>while</code>, в котором осуществляется контроль входа (пусть Мэри) в систему:</p>
    <p><code>while sleep 60</code></p>
    <p><code>do</code></p>
    <p><code> who | grep mary</code></p>
    <p><code>done</code></p>
    <p>Команда <code>sleep</code>, устанавливающая паузу на 60 с, всегда выполняется нормально (если ее не прервали) и, значит, всегда возвращает код "успех", поэтому в цикле раз в минуту будет проверяться, находится ли Мэри в системе. Недостаток такого решения состоит в том, что если Мэри уже вошла в систему, то нужно ждать 60 с, чтобы узнать об этом. О продолжении же работы Мэри в системе каждую минуту будет поступать сообщение. Цикл можно перевернуть и записать с помощью <code>until</code>, чтобы получать информацию сразу без задержки, если Мэри в данный момент работает в системе:</p>
    <p><code>until who | grep mary do</code></p>
    <p><code> sleep 60</code></p>
    <p><code>done</code></p>
    <p>Теперь условие представляется более интересным. Если Мэри вошла в систему, то <code>'who | grep mary'</code> выдаст запись о ней из списка команды <code>who</code> и вернет код "истина". Это связано с тем, что <code>grep</code> выдает код завершения, показывающий, удалось ли ей найти что-нибудь, а код завершения конвейера есть код завершения последней команды.</p>
    <p>В заключение мы можем оформить команду, дав ей имя и установив в системе:</p>
    <p><code>$ cat watchfor</code></p>
    <p><code># watchfor: watch for someone to log in</code></p>
    <empty-line/>
    <p><code>PATH=/bin:/usr/bin case $# in</code></p>
    <empty-line/>
    <p><code>0) echo 'Usage: watchfor person' 1&gt;&amp;2; exit 1</code></p>
    <p><code>esac</code></p>
    <empty-line/>
    <p><code>until who | egrep "$1"</code></p>
    <p><code>do</code></p>
    <p><code> sleep 60</code></p>
    <p><code>done</code></p>
    <empty-line/>
    <p><code>$ cx watchfor</code></p>
    <p><code>$ watchfor you</code></p>
    <p><code>you tty0 Oct 1 08:01       </code>Работает</p>
    <p><code>$ mv watchfor /usr/you/bin </code>Установим в системе</p>
    <p><code>$</code></p>
    <p>Мы заменили <code>grep</code> на <code>egrep</code>, чтобы было можно задавать</p>
    <p><code>$ watchfor 'joe | mary'</code></p>
    <p>и следить за несколькими пользователями.</p>
    <p>Более сложный пример: можно контролировать вход в систему и выход из нее всех пользователей и сообщать обо всех фактах входа или выхода. Это можно рассматривать как некоторое дополнение к команде <code>who</code>. Основная идея проста: раз в минуту запускать команду <code>who</code> и сравнивать результат ее действия с результатом, полученным минутой ранее, сообщая обо всех различиях. Вывод команды <code>who</code> хранится в файле, и мы можем записывать его в каталог <code>/tmp</code>. Чтобы отличить свои файлы от файлов, принадлежащих другим процессам, в имена файлов вставляется переменная интерпретатора <code>$$</code> (номер процесса команды интерпретатора), что является обычной практикой. Имя команды упоминается во временных файлах главным образом для администратора системы. Часто команды (включая данную версию <code>watchfor</code>) оставляют после себя файлы в /tmp, и полезно знать, какая команда это сделала. Здесь <code>":"</code> — встроенная команда, которая</p>
    <p><code>$ cat watchwho</code></p>
    <p><code># watchwho: watch who logs in and out</code></p>
    <empty-line/>
    <p><code>PATH=/bin:/usr/bin</code></p>
    <p><code>new=/tmp/wwho1.$$</code></p>
    <p><code>old=/tmp/wwho2.$$</code></p>
    <p><code>&gt; $old # create an empty file</code></p>
    <empty-line/>
    <p><code>while : # loop forever</code></p>
    <p><code>do</code></p>
    <p><code> who &gt;$new</code></p>
    <p><code> diff $old $new</code></p>
    <p><code> mv $new $old</code></p>
    <p><code> sleep 60</code></p>
    <p><code>done | awk '/&gt;/ { $1 = "in: "; print }</code></p>
    <p><code>            /&lt;/ { $1 = "out: "; print }'</code></p>
    <p><code>$</code></p>
    <p>только обрабатывает свои аргументы и возвращает код "истина". Мы могли бы заменить ее командой <code>true</code>, просто передающей код завершения "истина" (есть также команда <code>false</code>), но команда <code>':'</code> более эффективна, поскольку не нужно выполнять эту команду, выбирая ее из файловой системы.</p>
    <p>В выводе команды <code>diff</code> используются символы <code>&lt;</code> и <code>&gt;</code> для разделения данных из двух файлов. Программа, написанная на языке <code>awk</code>, обрабатывает результаты, чтобы сообщить об изменениях в более понятном формате. Обратите внимание на то, что весь цикл передает результаты работы по конвейеру <code>awk</code> программе, вместо того, чтобы запускать заново <code>awk</code> программу каждую минуту. Для такой обработки редактор <code>sed</code> не подходит, поскольку его вывод всегда задерживается по сравнению с входным потоком на одну строку: всегда есть одна входная строка, которая уже обработана, но не напечатана, а это приводит к ненужной задержке.</p>
    <p>Поскольку файл <code>old</code> создается пустым, первый вывод команды <code>watchfor</code> содержит весь список пользователей, находящихся в системе в данный момент. Замена команды, которая создает файл <code>old</code>, на <code>who &gt; $old</code> приведет к тому, что <code>watchfor</code> выдаст только изменения, но это уже — дело вкуса.</p>
    <p>Другая программа в цикле следит за содержимым вашего почтового ящика: как только оно изменяется, программа выдает сообщение: "You have a mail" ("У вас есть почта"). Такая программа является полезной альтернативой встроенному в интерпретатор механизму, использующему переменную <code>MAIL</code>. Чтобы показать другой стиль программирования, мы реализовали ее с помощью переменных интерпретатора, а не файлов:</p>
    <p><code>$ cat checkmail</code></p>
    <p><code># checkmail: watch mailbox for growth</code></p>
    <empty-line/>
    <p><code>PATH=/bin:/usr/bin</code></p>
    <p><code>MAIL=/usr/spool/mail/`getname` # system dependent</code></p>
    <empty-line/>
    <p><code>t=${1-60}</code></p>
    <empty-line/>
    <p><code>x="`ls -l $MAIL`"</code></p>
    <p><code>while :</code></p>
    <p><code>do</code></p>
    <p><code> y="`ls -l $MAIL`"</code></p>
    <p><code> echo $x $y</code></p>
    <p><code> x="$y"</code></p>
    <p><code> sleep $t</code></p>
    <p><code>done | awk '$4 &lt; $12 { print "You have mail" }'</code></p>
    <p><code>$</code></p>
    <p>Мы опять воспользовались <code>awk</code> программой, на этот раз — чтобы добиться вывода сообщения только в тех случаях, когда почтовый ящик пополняется, а не просто изменяется. Иначе вы получите сообщение сразу после исключения письма. (Версия, встроенная в интерпретатор, имеет такой недостаток.)</p>
    <p>Обычно интервал времени устанавливается равным 60 с, но если командная строка содержит параметр, например</p>
    <p><code>$ chekmail 30</code></p>
    <p>то интервал задается им. Переменная интерпретатора принимает в качестве значения заданное параметрами время или 60 с, если время не задано, с помощью присваивания</p>
    <p><code>t=${1-60}</code></p>
    <p>Это еще одна возможность языка <code>shell</code>. <code>${var}</code> эквивалентно <code>$var</code> и может использоваться для преодоления трудностей, связанных с появлением переменных внутри буквенно-цифровых строк:</p>
    <p><code>$ var=hello</code></p>
    <p><code>$ varx=goodbye</code></p>
    <p><code>$ echo $var</code></p>
    <p><code>hello</code></p>
    <p><code>$ echo ${var}x</code></p>
    <p><code>hellox</code></p>
    <p><code>$</code></p>
    <p>Определенные символы внутри фигурных скобок задают специальную обработку переменной. В том случае, когда переменная не определена и за ее именем идет знак вопроса, выдается строка, следующая за символом <code>?</code>, и интерпретатор прекращает работу (если только он не работает в диалоговом режиме). При отсутствии строки печатается стандартное сообщение:</p>
    <p><code>$ echo ${var?}</code></p>
    <p><code>hello                   </code>все в порядке, var определено</p>
    <p><code>$ echo ${junk}</code></p>
    <p><code>junk: parameter not set </code>стандартное сообщение</p>
    <p><code>$ echo ${junk?error!}</code></p>
    <p><code>junk: error!            </code>строка задана</p>
    <p><code>$</code></p>
    <p>Отметим, что в сообщении, выдаваемом интерпретатором, всегда указывается имя неопределенной переменной.</p>
    <p>В другой конструкции <code>${var-thing}</code> выбирается <code>$var</code>, если оно определено, и <code>thing</code> — в противном случае. В подобной конструкции <code>${var-thing}</code> значение <code>$var</code> также устанавливается равным <code>thing</code>:</p>
    <p><code>$ echo ${junk-'Hi there'}</code></p>
    <p><code>Hi there</code></p>
    <p><code>$ echo ${junk?)</code></p>
    <p><code>junk: parameter not set </code>значение junk не изменилось</p>
    <p><code>$ echo {junk='Hi there'}</code></p>
    <p><code>Hi there</code></p>
    <p><code>$ echo ${junk?}</code></p>
    <p><code>Hi there                </code>junk принял значение Hi there</p>
    <p><code>$</code></p>
    <p>Правила получения значений переменных приведены в табл. 5.3. Возвращаясь к нашему исходному примеру</p>
    <p><code>t=${1-60}</code></p>
    <p>видим, что <code>t</code> присваивается <code>$1</code> или 60, если аргумент не задан.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>$var</code></td>
      <td align="left" valign="top">Значение <code>var</code>; ничего, если <code>var</code> не определено</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>${var}</code></td>
      <td align="left" valign="top">То же; полезно, если за именем переменной следует буквенно-цифровая строка</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>${var-thing}</code></td>
      <td align="left" valign="top">Значение <code>var</code>, если оно определено; в противном случае — <code>thing</code>; <code>$var</code> не изменяется</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>${var=thing}</code></td>
      <td align="left" valign="top">Значение <code>var</code>, если оно определено; в противном случае — <code>thing</code>. Если <code>var</code> не определено, то <code>$var</code> присваивается <code>thing</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>${var?строка}</code></td>
      <td align="left" valign="top">Если <code>var</code> определено — <code>$var</code>; в противном случае выводится строка и интерпретатор прекращает работу. При пустой строке выводится: <code>var: parameter not set</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>${var+thing}</code></td>
      <td align="left" valign="top"><code>thing</code>, если <code>$var</code> определено; в противном случае — ничего</td>
     </tr>
    </table>
    <p>Таблица 5.3: <strong>Получение значений переменных в языке</strong></p>
    <empty-line/>
    <cite>
     <subtitle>Упражнение 5.9</subtitle>
     <p>Обратите внимание на реализацию команд <code>true</code> и <code>false</code> в <code>/usr/bin</code> или <code>/bin</code>. (Как бы вы определили, где они находятся?)</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.10</subtitle>
     <p>Измените команду <code>watchfor</code> так, чтобы пользователь мог задавать несколько имен, а не вводить <code>'joe|mary'</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.11</subtitle>
     <p>Напишите версию команды <code>watchwho</code>, которая использует команду <code>comm</code> вместо <code>awk</code> для сравнения новой и старой информации. Какая версия вам больше нравится?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.12</subtitle>
     <p>Напишите версию команды <code>watchwho</code>, в которой вывод команды <code>who</code> хранится в переменных языка <code>shell</code>, а не в файлах. Какая версия лучше? Какая версия быстрее работает? Следует ли в командах <code>watchwho</code> и <code>checkmail</code> автоматически использовать операцию <code>&amp;</code>?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.13</subtitle>
     <p>В чем состоит различие между пустой командой языка <code>shell</code>: и символом примечания <code>#</code>? Нужны ли они?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>5.4 Команда <code>trap</code>: обработка прерываний</p>
    </title>
    <p>Если во время выполнения команды <code>watchwho</code> нажать клавишу <emphasis>DEL</emphasis> (<emphasis>УДЛ</emphasis>) или отключить компьютер от сети, то один или несколько временных файлов останутся в каталоге <code>/tmp</code>. Команда <code>watchwho</code> удаляет временные файлы перед окончанием своей работы. Необходимы средства обнаружения таких ситуаций и восстановления после прерывания.</p>
    <p>При нажатии клавиши <emphasis>DEL</emphasis> всем процессам, запущенным с этого терминала, посылается <emphasis>сигнал прерывания</emphasis>. Аналогично в случае отключения посылается сигнал отбоя. Существуют и другие сигналы. Если в программе не предусмотрены специальные действия по обработке сигналов, то указанные сигналы прекращают ее выполнение. Интерпретатор защищает программы, запущенные с помощью <code>&amp;</code>, от прерываний, но не от отключений.</p>
    <p>В гл. 7 сигналы рассматриваются подробнее, но для работы с ними на языке <code>shell</code> глубоких знаний не требуется. Встроенная команда интерпретатора <code>trap</code> устанавливает последовательность команд, которая должна выполняться при возникновении сигнала:</p>
    <p><code>trap последовательность_команд список_номеров_сигналов</code></p>
    <p><emphasis>Последовательность команд</emphasis> — единый аргумент, поэтому его почти всегда нужно брать в кавычки. Номера сигналов обозначаются небольшими целыми числами, например, 2 соответствует сигналу, возникающему при нажатии клавиши <emphasis>DEL</emphasis>, а 1 — сигналу, возникающему при отключении от сети. Номера сигналов, наиболее часто используемых в <code>shell</code>-программах, приведены в табл. 5.4.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top">0</td>
      <td align="left" valign="top">Выход из интерпретатора (по любой причине, включая конец файла)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">1</td>
      <td align="left" valign="top">Отбой</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">2</td>
      <td align="left" valign="top">Прерывание (клавиша <emphasis>DEL</emphasis>)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">3</td>
      <td align="left" valign="top">Останов (<emphasis>ctl-\</emphasis>; вызывает распечатку содержимого памяти программы)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">9</td>
      <td align="left" valign="top">Уничтожение (нельзя перехватить или игнорировать)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">15</td>
      <td align="left" valign="top">Окончание выполнения; сигнал по умолчанию, производимый <code>kill(1)</code></td>
     </tr>
    </table>
    <p>Таблица 5.4: <strong>Номера сигналов в интерпретаторе</strong></p>
    <empty-line/>
    <p>Для удаления временных файлов в команде <code>watchwho</code> вызов команды <code>trap</code> должен указываться перед циклом, чтобы перехватить сигналы прерывания, отбоя и окончания выполнения:</p>
    <p><code>...</code></p>
    <p><code>trap 'rm -f $new $old; exit 1' 1 2 15</code></p>
    <p><code>while:</code></p>
    <p><code>...</code></p>
    <p>Последовательность команд, образующих первый аргумент команды <code>trap</code>, подобна вызову подпрограммы, который происходит сразу по возникновении сигнала. Когда эта последовательность окончится, прерванная программа возобновляется с места прерывания, если только сигнал не уничтожит ее. Таким образом, последовательность команд в <code>trap</code> должна явно вызывать <code>exit</code>, иначе <code>shell</code>-программа продолжит свое выполнение после прерывания. Кроме того, последовательность команд будет читаться дважды: при установке команды <code>trap</code> и при обращении к ней. Поэтому последовательность команд лучше защищать апострофами, чтобы значения переменных вычислялись только при выполнении программ, указанных в команде <code>trap</code>. В данном случае это не имеет значения, но позднее вы столкнетесь с ситуацией, когда это важно. Кстати, флаг <code>-f</code> предписывает команде <code>rm</code> не задавать вопросов.</p>
    <p>Иногда команду <code>trap</code> полезно применять в диалоговом режиме, чаще всего для того, чтобы не допустить уничтожения программы сигналом отбоя, возникшим при обрыве телефонной связи:</p>
    <p><code>$ (trap "" 1; долго_выполняемая команда) &amp;</code></p>
    <p><code>2134</code></p>
    <p><code>$</code></p>
    <p>Для процесса и его потомков пустая последовательность означает, что нужно игнорировать прерывания. При наличии скобок команда <code>trap</code> и <code>долго_выполняемая_команда</code> выполняются порожденным интерпретатором вместе и как фоновые; без них команда <code>trap</code> действовала бы на исходный интерпретатор, так же как и на долго_выполняемую_команду.</p>
    <p>Команда <code>nohup(1)</code> — небольшая <code>shell</code>-программа, обеспечивающая непрерывное выполнение команд. Ниже полностью приведен ее вариант из седьмой версии:</p>
    <p><code>$ cat 'which nohup'</code></p>
    <p><code>trap "" 1 15</code></p>
    <p><code>if test -t 2&gt;&amp;1</code></p>
    <p><code>then</code></p>
    <p><code> echo "Sending output to 'nohup.out'"</code></p>
    <p><code> exec nice -5 $* &gt;&gt;nohup.out 2&gt;&amp;1</code></p>
    <p><code>else</code></p>
    <p><code> exec nice -5 $* 2&gt;&amp;1</code></p>
    <p><code>fi</code></p>
    <p><code>$</code></p>
    <p>Команда <code>test -t</code> проверяет, направлен ли стандартный выходной поток на терминал, чтобы вы могли решить, следует ли его сохранять. Фоновая программа выполняется с помощью команды <code>nice</code>, что снижает ее приоритет по сравнению с диалоговыми программами. (Обратите внимание, что команда <code>nohup</code> не устанавливает значение <code>PATH</code>. А может быть, это нужно?)</p>
    <p>Команда <code>exec</code> использована только для повышения эффективности; команда <code>nice</code> может выполняться и без нее. <code>Exec</code> — встроенная команда интерпретаторов, которая заменяет процесс, играющий роль текущего интерпретатора, на указанную программу. Таким образом она избавляется от одного процесса, а именно от интерпретатора, обычно ожидающего завершения программы. Мы могли бы применять <code>exec</code> и в некоторых других программах, например в конце обобщенной программы <code>cal</code>, когда происходит обращение к <code>/usr/bin/cal</code>.</p>
    <p>Кстати, сигнал 9 — это тот сигнал, который нельзя перехватить или игнорировать: он всегда уничтожает процесс. На языке <code>shell</code> его посылка задается с помощью</p>
    <p><code>$ kill -9 номер_процесса</code></p>
    <p>Обращение <code>kill -9</code> не является стандартным, поскольку процессу, уничтоженному таким способом, не дается время для приведения в порядок своих дел перед "смертью".</p>
    <cite>
     <subtitle>Упражнение 5.14</subtitle>
     <p>В приведенной выше версии команды <code>nohup</code> стандартный поток диагностики команды соединяется со стандартным выходным потоком. Хорошее ли это решение? Если нет, то как бы вы разделили их явно?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.15</subtitle>
     <p>Найдите встроенную команду <code>times</code> и добавьте к вашему файлу строку <code>.profile</code>, чтобы при вашем выходе из системы интерпретатор выдавал использованное вами процессорное время.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.16</subtitle>
     <p>Напишите программу, находящую следующий свободный идентификатор пользователя в файле <code>/etc/passwd</code>. Если у вас есть энтузиазм (и право доступа), сделайте из нее команду, устанавливающую нового пользователя системы. Какие нужны для нее права доступа? Как следует ей обращаться с прерываниями?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>5.5 Команда <code>overwrite</code>: замена файла</p>
    </title>
    <p>В команде <code>sort</code> есть флаг <code>-о</code> для замены файла:</p>
    <p><code>$ sort файл1 -о файл2</code></p>
    <p>Ее эквивалент:</p>
    <p><code>$ sort файл1 &gt; файл2</code></p>
    <p>Если <code>файл1</code> и <code>файл2</code> — это один и тот же файл, то после операции переключения <code>&gt;</code> входной файл станет пустым перед сортировкой. Но с флагом <code>-о</code> команда выполняется правильно, потому что входной файл сортируется и сохраняется во временном файле перед созданием выходного файла.</p>
    <p>Могут использовать флаг <code>-о</code> и другие команды. Например, редактор <code>sed</code> может редактировать файл с заменой:</p>
    <p><code>$ sed 's/UNIX/UNIX (TM)/g' -o ch2</code> Так не получится!</p>
    <p>Непрактично изменять все подобные команды, вводя флаг — это не лучшее решение. Более целесообразным представляется централизованное выполнение функций, как в случае операции <code>&gt;</code> интерпретатора, для чего мы создадим программу <code>overwrite</code>. Первый ее вариант выглядит так:</p>
    <p><code>$ sed 's/UNIX/UNIX (TM)/g' гл2 | overwrite гл2</code></p>
    <p>В основном алгоритм программы очевиден: нужно только сохранить где-нибудь весь входной поток вплоть до конца файла, а затем копировать его в файл, указанный как аргумент:</p>
    <p><code># overwrite: copy standard input to output after EOF</code></p>
    <p><code># version 1. BUG here</code></p>
    <empty-line/>
    <p><code>PATH=/bin:/usr/bin</code></p>
    <empty-line/>
    <p><code>case $# in</code></p>
    <p><code>1) ;;</code></p>
    <p><code>*) echo 'Usage: overwrite file' 1&gt;&amp;2; exit 2</code></p>
    <p><code>esac</code></p>
    <empty-line/>
    <p><code>new=/tmp/overwr.$$</code></p>
    <p><code>trap 'rm -f $new; exit 1' 1 2 15</code></p>
    <empty-line/>
    <p><code>cat &gt;$new # collect the input</code></p>
    <p><code>cp $new $1 # overwrite the input file</code></p>
    <p><code>rm -f $new</code></p>
    <p>Команда <code>cp</code> используется вместо команды <code>mv</code>, чтобы не изменились права доступа и остался прежним владелец выходного файла, если он уже существует. Хотя этот вариант и чрезвычайно прост, здесь возможна "фатальная" ошибка: если пользователь нажмет клавишу <emphasis>DEL</emphasis> (<emphasis>УДЛ</emphasis>) во время выполнения команды <code>cp</code>, первоначальный выходной файл будет уничтожен. Необходимо соблюдать осторожность, поскольку прерывание может остановить замену входного файла:</p>
    <p><code># overwrite: copy standard input to output after EOF</code></p>
    <p><code># version 2. BUG here too</code></p>
    <empty-line/>
    <p><code>PATH=/bin:/usr/bin</code></p>
    <p><code>case $# in 1) ;;</code></p>
    <p><code>*) echo 'Usage: overwrite file' 1&gt;&amp;2; exit 2</code></p>
    <p><code>esac</code></p>
    <empty-line/>
    <p><code>new=/tmp/overwr1.$$</code></p>
    <p><code>old=/tmp/overwr2.$$</code></p>
    <p><code>trap 'rm -f $new $old; exit 1' 1 2 15</code></p>
    <empty-line/>
    <p><code>cat &gt;$new # collect the input</code></p>
    <p><code>cp $1 $old # save original file</code></p>
    <p><code>trap '' 1 2 15 # we are committed; ignore signals</code></p>
    <p><code>cp $new $1 # overwrite the input file</code></p>
    <empty-line/>
    <p><code>rm -f $new $old</code></p>
    <p>Если клавиша <emphasis>DEL</emphasis> будет нажата прежде, чем начнется работа с исходным файлом, то произойдет удаление временных файлов и файл останется один. После сохранения файла сигналы игнорируются, поэтому выполнение последней команды <code>cp</code> не прервется. Если команда <code>cp</code> начала выполняться, команда <code>overwrite</code> обязана заменить исходный файл.</p>
    <p>Здесь есть некоторая тонкость. Рассмотрим последовательность:</p>
    <p><code>$ sed 's/UNIX/UNIX(TM)g' special | overwrite special</code></p>
    <p><code>command garbled: s/UNIX(TM)g</code></p>
    <p><code>$ ls -l special</code></p>
    <p><code>-rw-rw-rw- 1 you 0 Oct 1 09:02 special #$%@*!</code></p>
    <p><code>$</code></p>
    <p>Если в программе, поставляющей входной поток для команды overwrite, произойдет ошибка, то выходной поток будет пустым, и <code>overwrite</code> обязательно (с сознанием выполненного долга) уничтожит файл, заданный в качестве аргумента.</p>
    <p>Во избежание такого финала можно предложить несколько решений. Команда <code>overwrite</code> могла бы запрашивать подтверждение перед заменой файла, но, сделав команду диалоговой, мы потеряем большую часть ее достоинств. Она могла бы проверять, что ее входной поток не пуст (с помощью <code>test -2</code>), но это некрасиво и к тому же неверно: выходной поток мог быть создан до обнаружения ошибки.</p>
    <p>Наилучшее решение заключается в том, чтобы выполнять программу, поставляющую данные, под контролем команды <code>overwrite</code>, чтобы можно было проверить ее код завершения. Это, правда, противоречит традициям и здравому смыслу: ведь в конвейере команда <code>overwrite</code> обычно должна быть последней, но для правильной работы она должна идти первой. Однако <code>overwrite</code> ничего не выдает в стандартный выходной поток, поэтому можно считать, что не происходит потери общности. Более того, ее синтаксис не является каким-то необычным: <code>time</code>, <code>nice</code>, <code>nohup</code> представляют собой команды, аргументами которых служат другие команды. Ниже приведен безопасный вариант:</p>
    <p><code># overwrite: copy standard input to output after EOF</code></p>
    <p><code># final version</code></p>
    <empty-line/>
    <p><code>opath=$PATH</code></p>
    <p><code>PATH=/bin:/usr/bin</code></p>
    <empty-line/>
    <p><code>case $# in</code></p>
    <p><code>0|1) echo 'Usage: overwrite file cmd [args]' 1&gt;&amp;2; exit 2</code></p>
    <p><code>esac</code></p>
    <empty-line/>
    <p><code>file=$1; shift</code></p>
    <p><code>new=/tmp/overwr1.$$; old=/tmp/overwr2.$$</code></p>
    <p><code>trap 'rm -f $new $old; exit 1' 1 2 15 # clean up files</code></p>
    <empty-line/>
    <p><code>if PATH=$opath "$@" &gt;$new # collect input</code></p>
    <p><code>then</code></p>
    <p><code> cp $file $old # save original file</code></p>
    <p><code> trap '' 1 2 15 # we are committed; ignore signals</code></p>
    <p><code> cp $new $file</code></p>
    <p><code>else</code></p>
    <p><code> echo "overwrite: $1 failed, $file unchanged" 1&gt;&amp;2 exit 1</code></p>
    <p><code>fi</code></p>
    <p><code>rm -f $new $old</code></p>
    <p>Встроенная команда интерпретатора <code>shift</code> сдвигает весь список аргументов на одну позицию влево: <code>$2</code> становится <code>$1</code>, <code>$3</code> становится <code>$2</code> и т.д. Строка обозначает все аргументы (после <code>shift</code>), как и <code>$*</code>, но без интерпретации; мы вернемся к ее рассмотрению в разд. 5.7.</p>
    <p>Заметьте, что значение <code>PATH</code> нужно восстановить перед выполнением команды пользователя; если этого не сделать, то команды, не находящиеся в <code>/bin</code> или <code>/usr/bin</code>, будут недоступны для <code>overwrite</code>.</p>
    <p>Теперь команда <code>overwrite</code> выполняется верно (хотя и она получилась несколько громоздкой):</p>
    <p><code>$ cat notice</code></p>
    <p><code>Unix is a Trademark of Bell Laboratories</code></p>
    <p><code>$ overwrite notice sed 's/UNIXUNIX(TM)/g' notice</code></p>
    <p><code>command garbled: s/UNIXUNIX(TM)/g</code></p>
    <p><code>overwrite: sed failed, notice unchanged</code></p>
    <p><code>$ cat notice</code></p>
    <p><code>UNIX is a Trademark of Bell Laboratories</code> He изменился</p>
    <p><code>$ overwrite notice sed 's/UNIX/UNIX(TM)/g' notice</code></p>
    <p><code>$ cat notice</code></p>
    <p><code>UNIX(TM) is a Trademark of Bell Laboratories</code></p>
    <p><code>$</code></p>
    <p>Типичной задачей является использование редактора <code>sed</code> для замены всех вхождений одного слова на другое слово. Имея под рукой команду <code>overwrite</code>, легко написать программу на языке <code>shell</code> для ее решения:</p>
    <p><code>$ cat replace</code></p>
    <p><code># replace: replace str1 in files with str2, in place</code></p>
    <empty-line/>
    <p><code>PATH=/bin:/usr/bin</code></p>
    <empty-line/>
    <p><code>case $# in</code></p>
    <p><code>0|1|2) echo 'Usage: replace str1 str2 files' 1&gt;&amp;2; exit 1</code></p>
    <p><code>esac</code></p>
    <empty-line/>
    <p><code>left="$1"; right="$2"; shift; shift</code></p>
    <empty-line/>
    <p><code>for i do</code></p>
    <p><code> overwrite $i sed "s@$left@$right@g" $i</code></p>
    <p><code>done</code></p>
    <p><code>$ cat footnote</code></p>
    <p><code>UNIX is not an acronym</code></p>
    <p><code>$ replace UNIX Unix footnote</code></p>
    <p><code>$ cat footnote</code></p>
    <p><code>Unix is not an acronym</code></p>
    <p><code>$</code></p>
    <p>(Вспомните: если список в цикле <code>for</code> пуст, то по умолчанию он равен <code>$*</code>.) Мы использовали <code>@</code> вместо <code>/</code> для разбиения в команде подстановки, поскольку менее вероятно, что <code>@</code> вступит в конфликт с входной строкой. Команда <code>replace</code> устанавливает <code>PATH</code> равным <code>/bin:/usr/bin</code>, исключая <code>$HOME/bin</code>. Это означает, что <code>overwrite</code> должна находиться в <code>/usr/bin</code>, чтобы команда <code>replace</code> сработала. Мы сделали такое предположение для простоты; если вы не можете поместить <code>overwrite</code> в <code>/usr/bin</code>, вам придется добавить <code>$HOME/bin</code> к <code>PATH</code> в команде <code>replace</code> или явно задать полное имя <code>overwrite</code>. В дальнейшем будем полагать, что команды, которые мы создаем, находятся в <code>/usr/bin</code>, где им и следует быть.</p>
    <cite>
     <subtitle>Упражнение 5.17</subtitle>
     <p>Почему команда <code>overwrite</code> не использует сигнал 0 в команде <code>trap</code>, чтобы файлы удалялись при выходе из нее? Подсказка: попробуйте нажать клавишу <emphasis>DEL</emphasis> во время выполнения следующей программы:</p>
     <p><code>trap "echo exiting; exit 1" 0 2</code></p>
     <p><code>sleep 10</code></p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.18</subtitle>
     <p>Добавьте флаг <code>-v</code> к команде <code>replace</code> для вывода всех измененных строк на <code>/dev/tty</code>.</p>
     <p><emphasis>Подсказка</emphasis>: <code>s/$left/$right/g $vflag</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.19</subtitle>
     <p>Увеличьте надежность команды <code>replace</code>, чтобы ее выполнение не зависело от символов в строке замены.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.20</subtitle>
     <p>Можно ли использовать <code>replace</code> для замены <code>i</code> на <code>index</code> всюду в программе? Какие вы внесли бы изменения, чтобы добиться этого?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.21</subtitle>
     <p>Достаточно ли команда <code>replace</code> эффективна и удобна, чтобы находиться в каталоге <code>/usr/bin</code>? Не лучше ли вводить по мере необходимости подходящие команды редактора <code>sed</code> (да или нет)? Обоснуйте свой ответ.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.22</subtitle>
     <p>(<emphasis>Усложненное</emphasis>.) Команда</p>
     <p><code>$ overwrite файл 'who | sort'</code></p>
     <p>не выполняется. Объясните причину этого и исправьте ее. <emphasis>Подсказка</emphasis>: посмотрите <code>eval</code> в справочном руководстве по <code>sh(1)</code>. Как ваше решение повлияет на интерпретацию специальных символов в команде?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>5.6 Команда <code>zap</code>: уничтожение процесса по имени</p>
    </title>
    <p>Команда <code>kill</code> только завершает процесс с указанным номером. Если нужно уничтожить определенный фоновый процесс, обычно приходится выполнить команду <code>ps</code>, чтобы узнать номер процесса, а затем ввести этот номер в качестве аргумента для команды <code>kill</code>. Однако нелепо иметь программу, выдающую номер процесса, который сразу же передается вручную другой программе. Имеет смысл написать программу, скажем <code>zap</code>, для автоматического выполнения такой работы. Здесь, правда, есть одно препятствие: уничтожение процессов опасно, поэтому следует принять меры для обеспечения сохранности нужных процессов. Хорошей защитой всегда служат диалоговое выполнение zap и использование команды <code>pick</code> для выбора "жертв".</p>
    <p>Кратко напомним вам о команде <code>pick</code>: она выдает поочередно свои аргументы, спрашивая ответ у пользователя; если ответ — <code>y</code>, то аргумент выводится (команда <code>pick</code> обсуждается в следующем разделе). В нашем случае <code>pick</code> используется для подтверждения, что процессы, выбранные по имени, — именно те, которые пользователь хочет уничтожить:</p>
    <p><code>$ cat zap</code></p>
    <p><code># zap pattern: kill all processes matching pattern</code></p>
    <p><code># BUG in this version</code></p>
    <empty-line/>
    <p><code>PATH=/bin:/usr/bin</code></p>
    <empty-line/>
    <p><code>case $# in</code></p>
    <p><code>0) echo 'Usage: zap pattern' 1&gt;&amp;2; exit 1</code></p>
    <p><code>esac</code></p>
    <empty-line/>
    <p><code>kill `pick \`ps -ag | grep "$*"\` | awk '{print $1}'`</code></p>
    <p>Обратите внимание на вложенные знаки слабого ударения, защищенные символами обратной дробной черты, <code>awk</code> программа выделяет номер процесса из выходных данных команды <code>ps</code>, выбранной с помощью <code>pick</code>:</p>
    <p><code>$ sleep 1000 &amp;</code></p>
    <p><code>2216</code></p>
    <p><code>$ ps -ag</code></p>
    <p><code> PID TTY TIME CMD</code></p>
    <p><code>...</code></p>
    <p><code>2216   0 0:00 sleep 1000</code></p>
    <p><code>...</code></p>
    <p><code>$ zap sleep</code></p>
    <p><code>2216?</code></p>
    <p><code>0? q</code> Что происходит?</p>
    <p><code>$</code></p>
    <p>Проблема состоит в том, что выходные данные команды <code>ps</code> разбиты на слова, которые воспринимаются и обрабатываются командой <code>pick</code> как отдельные аргументы вместо того, чтобы обрабатываться сразу по строке. Обычная процедура интерпретатора заключается в разбиении строк на аргументы с границами пробел/не пробел, как показано ниже:</p>
    <p><code>for i in 1 2 3 4 5</code></p>
    <p>В этой программе нужно контролировать процесс разбиения интерпретатором строк на аргументы, чтобы только символ перевода строки разделял соседние "слова".</p>
    <p>Внутренняя переменная интерпретатора <code>IFS</code> (internal field separator — внутренний разделитель полей) представляет собой строку символов, которая разделяет слова в списке аргументов, находящихся в знаках слабого ударения или циклах <code>for</code>. Обычно <code>IFS</code> содержит пробелы, символы табуляции и конца строки, но мы можем заменить ее на что-либо нужное, например просто на символ перевода строки:</p>
    <p><code>$ echo 'echo $#' &gt;nargs</code></p>
    <p><code>$ cx nargs</code></p>
    <p><code>$ who</code></p>
    <p><code>you tty0 Oct 1 05:59</code></p>
    <p><code>pjw tty2 Oct 1 11:26</code></p>
    <p><code>$ nargs 'who'</code></p>
    <p><code>10 </code>10 полей, разделенных пробелом и концом строки</p>
    <p><code>$ IFS='</code></p>
    <p><code>'  </code>Только конец строки</p>
    <p><code>$ nargs `who`</code></p>
    <p><code>2  </code>Две строки, два поля</p>
    <p><code>$</code></p>
    <p>После установки <code>IFS</code> равным символу перевода строки команда <code>zap</code> выполняется отлично:</p>
    <p><code>$ cat zap</code></p>
    <p><code># zap pat: kill all processes matching pat</code></p>
    <p><code># final version</code></p>
    <empty-line/>
    <p><code>PATH=/bin:/usr/bin IFS='</code></p>
    <p><code>' # just a newline</code></p>
    <empty-line/>
    <p><code>case $1 in</code></p>
    <p><code>"") echo 'Usage: zap [-2] pattern' 1&gt;&amp;2; exit 1 ;;</code></p>
    <p><code>-*) SIG=$1; shift</code></p>
    <p><code>esac</code></p>
    <empty-line/>
    <p><code>echo ' PID TTY TIME CMD'</code></p>
    <p><code>kill $SIG `pick \`ps -ag | egrep "$*"\` | awk '{print $1}`"</code></p>
    <p><code>$ ps -ag</code></p>
    <p><code>PID  TTY TIME CMD</code></p>
    <p><code>...</code></p>
    <p><code>2216   0 0:00 sleep 1000</code></p>
    <p><code>...</code></p>
    <p><code>$ zap sleep</code></p>
    <p><code>PID  TTY TIME CMD</code></p>
    <p><code>2216   0 0:00 sleep 1000? y</code></p>
    <p><code>2314   0 0:02 egrep sleep? N</code></p>
    <p><code>$</code></p>
    <p>Мы здесь кое-что добавили: необязательный аргумент, обозначающий сигнал (обратите внимание на то, что <code>SIG</code> будет неопределенным, а значит, должен рассматриваться как пустая строка, если аргумент не задан), а также <code>egrep</code> вместо <code>grep</code>, чтобы разрешить более сложные шаблоны типа <code>'sleep | date'</code>. Первая команда <code>echo</code> выдает столбец из заголовков выходных данных команды <code>ps</code>.</p>
    <p>Вас может заинтересовать, почему эта команда называется <code>zap</code>, а не просто <code>kill</code>. Основная причина заключается в том, что в отличие от случая с командой <code>cal</code> мы не даем действительно новой команды <code>kill</code>: <code>zap</code> по необходимости является диалоговой командой, с одной стороны, а с другой — мы хотим сохранить имя <code>kill</code> для настоящей команды. К тому же <code>zap</code> чрезвычайно медленна из-за накладных расходов на все дополнительные программы, хотя самую длинную по времени реализации команду <code>ps</code> все равно нужно выполнять. В следующей главе будет продемонстрировано более эффективное решение.</p>
    <cite>
     <subtitle>Упражнение 5.23</subtitle>
     <p>Измените команду <code>zap</code> так, чтобы она, выдавая заголовки из команды <code>ps</code>, была не чувствительна к изменениям в формате вывода <code>ps</code>. Насколько это усложнит программу?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>5.7 Команда <code>pick</code>: пробелы или аргументы</p>
    </title>
    <p>Вы уже достаточно подготовлены для того, чтобы написать команду <code>pick</code> на языке <code>shell</code>. Единственным новым средством является механизм чтения входного потока пользователя. Встроенная команда интерпретатора <code>read</code> читает одну строку текста из стандартного входного потока и присваивает ее (без перевода строки) в качестве значения указанной переменной:</p>
    <p><code>$ read greeting</code></p>
    <p><code>hello, world</code> Вводим новое значение для приветствия</p>
    <p><code>$ echo $greeting</code></p>
    <p><code>hello, world</code></p>
    <p><code>$</code></p>
    <p>Самым типичным примером использования команды <code>read</code> в файле <code>.profile</code> служит установка значений переменных среды при входе в систему, прежде всего установка переменных интерпретатора типа <code>TERM</code>.</p>
    <p>Команда <code>read</code> может читать только из стандартного входного потока; его нельзя даже переключить. Ни одну из встроенных команд интерпретатора (в отличие от основных структур управления типа <code>for</code>) нельзя переключить с помощью операций <code>&gt;</code> или <code>&lt;</code>:</p>
    <p><code>$ read greeting &lt;/etc/passwd</code></p>
    <p><code>goodbye          </code>Тем не менее надо ввести значение</p>
    <p><code>illegal io       </code>Сейчас shell сообщает об ошибке</p>
    <p><code>$ echo $greeting </code>greeting получает введенное значение,</p>
    <p><code>goodbye          </code>а не значение из файла</p>
    <p><code>$</code></p>
    <p>Это можно считать ошибкой интерпретатора, но такова жизнь. К счастью, можно предусмотреть переключение в цикле, охватывающем команду <code>read</code>, что является основным принципом реализации команды <code>pick</code>:</p>
    <p><code># pick: select arguments</code></p>
    <empty-line/>
    <p><code>PATH=/bin:/usr/bin</code></p>
    <empty-line/>
    <p><code>for i # for each argument</code></p>
    <p><code>do</code></p>
    <p><code> echo -n "$i? " &gt;/dev/tty</code></p>
    <p><code> read response</code></p>
    <p><code> case $response in</code></p>
    <p><code> y*) echo $i ;;</code></p>
    <p><code> q*) break</code></p>
    <p><code> esac</code></p>
    <p><code>done &lt;/dev/tty</code></p>
    <p>Обращение <code>echo -n</code> подавляет заключительный символ перевода строки, так что переменную <code>response</code> можно вывести на той же строке, что и приглашение. Конечно, приглашения выдаются на устройство <code>/dev/tty</code>, поскольку стандартный выходной поток, по всей вероятности, не выводится на терминал.</p>
    <p>Оператор <code>break</code> заимствован из языка Си: он завершает выполнение самого внутреннего цикла, в нашем случае <code>for</code>, когда вводится <code>q</code>. Мы выбрали символ <code>q</code> как сигнал прекращения процесса выбора потому, что это легко сделать, потенциально удобно и не противоречит другим программам.</p>
    <p>Интересно поэкспериментировать с пробелами в аргументах для команды <code>pick</code>:</p>
    <p><code>$ pick '1 2' 3</code></p>
    <p><code>1 2?</code></p>
    <p><code>3?</code></p>
    <p><code>$</code></p>
    <p>Если вы хотите узнать, как команда <code>pick</code> читает свои аргументы, запустите ее и нажмите клавишу <emphasis>RETURN</emphasis> после каждого приглашения. В том виде, в каком написана эта команда, она выполняется отлично: в цикле <code>for i</code> аргументы обрабатываются правильно. Мы могли бы написать цикл другими способами:</p>
    <p><code>$ grep for pick</code> Выясните, что делает эта версия</p>
    <p><code>for i in $*</code></p>
    <p><code>$ pick '1 2' 3</code></p>
    <p><code>1?</code></p>
    <p><code>2?</code></p>
    <p><code>3?</code></p>
    <p><code>$</code></p>
    <p>Эта версия не работаете поскольку операнды в цикле снова распознаются, а наличие пробелов в первом аргументе приводит к тому, что он разбивается на два аргумента. Попробуйте взять в кавычки <code>$*</code>:</p>
    <p><code>$ grep for pick</code> Попробуем другую версию</p>
    <p><code>for i in "$*"</code></p>
    <p><code>$ pick '1 2' 3</code></p>
    <p><code>1 2 3?</code></p>
    <p><code>$</code></p>
    <p>Такая версия тоже не работает, поскольку "<code>$*</code>" является единым словом, которое образовано из всех аргументов, объединенных вместе с разделяющими пробелами. Но решение все-таки есть (это почти черная магия): строка трактуется особым образом интерпретатором и преобразуется в нужное число аргументов для командного файла:</p>
    <p><code>$ grep for pick</code> Попробуем третью версию</p>
    <p><code>for i in "$@" '</code></p>
    <p><code>$ pick '1 2' 3</code></p>
    <p><code>1 2?</code></p>
    <p><code>3?</code></p>
    <p><code>$</code></p>
    <p>Строка <code>$@</code>, не взятая в кавычки, идентична <code>$*</code>; она обрабатывается иначе, только если заключена в кавычки. Мы использовали ее в команде <code>overwrite</code>, чтобы сохранить аргументы для команды пользователя.</p>
    <p>В итоге мы можем сформулировать следующие правила: <code>$*</code> и <code>$@</code> раскрываются как аргументы и снова распознаются; наличие пробелов в аргументах приводит к разбиению их на несколько аргументов;</p>
    <p>• "<code>$*</code>" является единым словом, которое образовано из всех аргументов командного файла, объединенных вместе с пробелами;</p>
    <p>• «<code>$*</code>» идентично аргументам, получаемым командным файлом: пробелы в аргументах игнорируются, в результате получается список слов, идентичных исходным аргументам.</p>
    <p>Если команда <code>pick</code> не имеет аргументов, она, по-видимому, должна читать стандартный входной поток, поэтому можно задать</p>
    <p><code>$ pick &lt; mailinglist</code></p>
    <p>вместо</p>
    <p><code>$ pick `cat mailinglist`</code></p>
    <p>Но мы не будем исследовать эту версию команды <code>pick</code> во избежание некоторых неприятных осложнений. Кроме того, значительно проще написать такую же программу на Си. С ней вы познакомитесь в следующей главе.</p>
    <p>Первые два из приведенных ниже упражнений достаточно сложны, но полезны даже для опытных программистов, работающих на языке <code>shell</code>.</p>
    <cite>
     <subtitle>Упражнение 5.24</subtitle>
     <p>Попробуйте написать программу <code>pick</code>, которая читает аргументы из стандартного входного потока, если ничего не задано в командной строке. Она должна правильно обрабатывать пробелы. Будет ли допустим ответ <code>q</code>? Если нет, то попытайтесь выполнить следующее упражнение.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.25</subtitle>
     <p>Хотя встроенные команды интерпретатора, такие, как <code>read</code> и <code>set</code>, нельзя переключить, можно временно переключить сам интерпретатор. Прочтите в справочном руководстве раздел по <code>sh(1)</code>, в котором описывается команда <code>exec</code>, и придумайте, как читать из <code>/dev/tty</code> без вызова порожденного интерпретатора. (Может оказаться полезным сначала прочитать гл. 7.)</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.26</subtitle>
    </cite>
    <p>(<emphasis>Более простое</emphasis>.) Используйте команду <code>read</code> в вашем файле <code>.profile</code> для инициации <code>TERM</code>, а также всего, что зависит от нее, например позиции табуляции.</p>
   </section>
   <section>
    <title>
     <p>5.8 Команда <code>news</code>: служба информации пользователей</p>
    </title>
    <p>В гл. 1 упоминалось о том, что в вашей системе может быть команда <code>news</code> для передачи сообщений, представляющих интерес для всех пользователей системы. Хотя названия команды и ее детали могут различаться, большинство систем имеет службу информации. Мы рассматриваем команду <code>news</code> не для замены вашей местной команды, а чтобы показать, как легко написать такую программу на языке <code>shell</code>. Неплохо было бы сравнить реализацию предлагаемой здесь команды news с вашей версией.</p>
    <p>Обычно основная идея таких программ заключается в том, что отдельные фрагменты новостей хранятся по одному в файлах в специальном каталоге типа <code>/usr/news</code>. Наша команда <code>news</code> сравнивает время изменения файлов в каталоге <code>/usr/news</code> и вашем исходном каталоге (<code>.news_time</code>). В целях отладки мы можем использовать каталог <code>'.'</code> как для файлов новостей, так и для <code>news_time</code>. Можно заменить его на <code>/usr/news</code>, когда программа будет готова для общего пользования:</p>
    <p><code>$ cat news</code></p>
    <p><code># news: print news files, version 1</code></p>
    <empty-line/>
    <p><code>HOME=. # debugging only</code></p>
    <p><code>cd . # place holder for /usr/news</code></p>
    <p><code>for i in `ls -t * $HOME/.news_time`</code></p>
    <p><code>do</code></p>
    <p><code> case $i in</code></p>
    <p><code> */.news_time) break ;;</code></p>
    <p><code> *) echo news: $i</code></p>
    <p><code>esac</code></p>
    <p><code>done</code></p>
    <p><code>touch $HOME/.news_time</code></p>
    <p><code>$ touch .news-time</code></p>
    <p><code>$ touch x</code></p>
    <p><code>$ touch y</code></p>
    <p><code>$ news</code></p>
    <p><code>news: y</code></p>
    <p><code>news: x</code></p>
    <p><code>$</code></p>
    <p>Команда <code>touch</code> заменяет время последней модификации файла, заданного в качестве аргумента, на настоящее время, не подвергая сам файл модификации. Для отладки мы даем только эхо имен файлов новостей, а не печатаем их. Цикл завершается при обнаружении <code>news_time</code>, тем самым перечисляются только файлы со свежими новостями. Заметьте, что символ <code>*</code> в операторе <code>case</code> может быть сопоставлен с <code>/</code>, что недопустимо для шаблонов имен файлов. А что будет, если <code>news_time</code> не существует?</p>
    <p><code>$ rm .news_time</code></p>
    <p><code>$ news</code></p>
    <p><code>$</code></p>
    <p>Отсутствие ответа удивляет и является ошибочным. Это вызвано тем, что когда команда <code>ls</code> не находит файл, она выдает соответствующее сообщение в стандартный выходной поток прежде, чем вывести какую-либо информацию о существующих файлах. Такая ситуация, безусловно, ошибочна — диагностические сообщения должны передаваться в стандартный файл диагностики. Но мы можем обнаружить эту ситуацию в цикле и переключить стандартный файл диагностики на стандартный выходной поток, так что все версии будут работать одинаково. (Данная проблема решена в новой версии, но мы рассмотрели ее, чтобы проиллюстрировать, как легко устранить недоделки.)</p>
    <p><code>$ cat news</code></p>
    <p><code># news: print news files, version 2</code></p>
    <empty-line/>
    <p><code>HOME=. # debugging only</code></p>
    <p><code>cd . # place holder for /usr/news</code></p>
    <p><code>IFS='</code></p>
    <p><code>' # just a newline</code></p>
    <p><code>for i in `ls -t * $HOME/.news_time 2&gt;&amp;1`</code></p>
    <p><code>do</code></p>
    <p><code> case $i in</code></p>
    <p><code> *' not found') ;;</code></p>
    <p><code> */.news_time) break ;;</code></p>
    <p><code> *) echo news: $i ;;</code></p>
    <p><code>esac</code></p>
    <p><code>done</code></p>
    <p><code>touch $HOME/.news_time</code></p>
    <p><code>$ news</code></p>
    <p><code>news: news</code></p>
    <p><code>news: y</code></p>
    <p><code>news: x</code></p>
    <p><code>$</code></p>
    <p>Мы должны были установить <code>IFS</code> равным символу конца строки, чтобы сообщение</p>
    <p><code>./.news_time not found</code></p>
    <p>не распознавалось как три слова.</p>
    <p>Команда <code>news</code> должна выводить на печать файлы новостей, а не создавать эхо их имен. Полезно знать, кто и когда послал сообщение, поэтому мы воспользуемся командами <code>set</code> и <code>ls -l</code> для вывода заголовка перед самим сообщением:</p>
    <p><code>$ ls -l news</code></p>
    <p><code>-rwxrwxrwx 1 you 208 Oct 1 12:05 news</code></p>
    <p><code>$ set `ls -l news`</code></p>
    <p><code>-rwxrwxrwx: bad option(s)</code> Что-то неправильно!</p>
    <p><code>$</code></p>
    <p>Это один из тех случаев, когда взаимозаменяемость программы и данных на языке <code>shell</code> имеет значение. Команда <code>set</code> "ругается", потому что ее аргумент ("<code>-rwxrwxrwx</code>") начинается с минуса и, следовательно, выглядит как флаг. Очевидным (хотя и неэлегантным) решением было бы предварить аргумент обычным символом:</p>
    <p><code>$ set X`ls -l news`</code></p>
    <p><code>$ echo "news: ($3) $5 $6 $7"</code></p>
    <p><code>news: (you) Oct 1 12:05</code></p>
    <p><code>$</code></p>
    <p>Здесь представлен разумный формат с указанием автора и даты сообщения вместе с именем файла. Приведем окончательный вариант команды <code>news</code>:</p>
    <p><code># news: print news files, final version</code></p>
    <empty-line/>
    <p><code>PATH=/bin:/usr/bin</code></p>
    <p><code>IFS='</code></p>
    <p><code>' # just a newline</code></p>
    <p><code>cd /usr/news</code></p>
    <empty-line/>
    <p><code>for i in `ls -t * $HOME/.news_time 2&gt;&amp;1`</code></p>
    <p><code>do</code></p>
    <p><code> IFS=' '</code></p>
    <p><code> case $i in</code></p>
    <p><code> *' not found') ;;</code></p>
    <p><code> */.news_time) break ;;</code></p>
    <p><code> *) set X`ls -l $i`</code></p>
    <p><code>  echo "</code></p>
    <p><code>   $i: ($3) $5 $6 $7</code></p>
    <p><code>  "</code></p>
    <p><code>  cat $i</code></p>
    <p><code> esac</code></p>
    <p><code>done</code></p>
    <p><code>touch $HOME/.news_time</code></p>
    <p>Дополнительные символы перевода строк разделяют в заголовке при печати фрагменты новостей. Первым значением <code>IFS</code> является символ перевода строки, поэтому сообщение <code>not found</code> из вывода первой команды <code>ls</code> (если оно есть) рассматривается как один аргумент. Во втором случае переменной <code>IFS</code> присваивается пробел, поэтому вывод второй команды <code>ls</code> разбивается на несколько аргументов.</p>
    <cite>
     <subtitle>Упражнение 5.27</subtitle>
     <p>Добавьте в команду news флаг <code>-n</code> ("notify" — извещение), чтобы сообщать о новостях, но не печатать их, и не выполняйте <code>touch .news_time</code>. Эту команду можно поместить в ваш файл <code>.profile</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.28</subtitle>
     <p>Сравните предложенный здесь подход и реализацию команды news с аналогичной командой вашей системы.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>5.9 Команды <code>get</code> и <code>put</code>: контроль изменении файла</p>
    </title>
    <p>В последнем разделе этой длинной главы мы приведем большой и более сложный пример, в котором продемонстрируем вам взаимодействие языков <code>shell</code>, <code>awk</code> и <code>sed</code>.</p>
    <p>Программа развивается по мере того, как мы устраняем ошибки и добавляем в нее новые средства. Иногда полезно сохранять ее разные версии, особенно в ситуации, когда кто-то переносит программу на другую машину, и возникает вопрос: "Что изменилось с тех пор, как мы получили версию вашей программы?" или "Как вы устранили такие- то ошибки?" К тому же наличие копий упрощает эксперимент: если у вас что-либо не получилось, то можно безболезненно вернуться к исходной программе.</p>
    <p>Одно из решений состоит в том, чтобы хранить копии всех версий программы, но это трудно организовать и, кроме того, требует большого объема памяти на диске. Мы же будем основываться на подобии последовательных версий, что позволяет хранить только их общую часть. Команда</p>
    <p><code>$ diff -е old new</code></p>
    <p>порождает список команд редактора <code>ed</code>, преобразующих файл <code>old</code> в <code>new</code>. Таким образом, можно хранить все версии на базе одного файла, сохраняя одну полную версию и множество команд редактирования, преобразующих ее в любую другую версию.</p>
    <p>Существуют два очевидных решения: хранить целиком последнюю версию и иметь команды редактирования для возврата к старым версиям или хранить самую старую версию и иметь команды редактирования для перехода к новым. Хотя второе решение чуть проще запрограммировать, первое предпочтительнее, поскольку из множества версий почти всегда интереснее выбрать новые.</p>
    <p>Мы рассмотрим первое решение. В едином файле, называемом файлом истории, хранится текущая версия, за которой следует множество команд редактирования, преобразующих каждую версию в предыдущую (т.е. более старую). Любой набор команд редактирования начинается такой строкой:</p>
    <p><code>@@@ пользователь дата сводка</code></p>
    <p><emphasis>Сводка</emphasis> — это одна строка, которая вводится пользователем и описывает изменения.</p>
    <p>Для работы с версиями используются две команды: <code>get</code> выделяет версию из файла истории, a <code>put</code> заносит новую версию в файл истории после запроса на ввод сводки изменений. Прежде чем привести программу, покажем, как выполняются <code>get</code> и <code>put</code> и как сохраняется файл истории:</p>
    <p><code>$ echo строка текста &gt; junk</code></p>
    <p><code>$ put junk</code></p>
    <p><code>Summary: создадим новый файл        </code>Введите описание</p>
    <p><code>get: no file junk.H                 </code>Файл-история не существует</p>
    <p><code>put: creating junk.H                </code>…и put создает его</p>
    <p><code>$ cat junk.H</code></p>
    <p><code>строка текста</code></p>
    <p><code>@@@ you Sat Oct 1 13:31:03 EDT 1983 </code>сделаем новый файл</p>
    <p><code>$ echo еще строка &gt;&gt;junk</code></p>
    <p><code>$ put junk</code></p>
    <p><code>Summary: одна строка добавлена</code></p>
    <p><code>$ cat junk.H</code></p>
    <p><code>строка текста</code></p>
    <p><code>еще одна строка текста</code></p>
    <p><code>@@@ you Sat Oct 1 13:31:28 EDT 1983 одна строка добавлена</code></p>
    <p><code>2d</code></p>
    <p><code>@@@ you Sat Oct 1 13:31:03 EDT 1983 сделаем новый файл</code></p>
    <p><code>$</code></p>
    <p>Команды редактирования представляют собой одну строку 2, которая исключает вторую строку файла, преобразуя новую версию в исходную:</p>
    <p><code>$ rm junk</code></p>
    <p><code>$ get junk                  </code>Самая новая версия</p>
    <p><code>$ cat junk строка текста еще строка текста</code></p>
    <p><code>$ get -l junk</code></p>
    <p><code>$ cat junk                  </code>Версия новейшая, но одна</p>
    <p><code>строка текста</code></p>
    <p><code>$ get junk                  </code>Опять самая новая версия</p>
    <p><code>$ replace еще 'другая' junk </code>Изменим ее</p>
    <p><code>$ put junk</code></p>
    <p><code>Summary: изменена вторая строка</code></p>
    <p><code>$ cat junk.H</code></p>
    <p><code>строка текста</code></p>
    <p><code>другая строка</code></p>
    <p><code>@@@ you Sat Oct 1 13:34:07 EDT 1983 одна строка добавлена</code></p>
    <p><code>2d</code></p>
    <p><code>@@@ you Sat Oct 1 13:31:03 EDT 1983 создадим новый файл</code></p>
    <p><code>$</code></p>
    <p>Для получения нужной версии файла в файле истории записаны команды редактирования. Первая группа команд преобразует самую последнюю версию в предыдущую, вторая группа преобразует предыдущую в пред-предыдущую версию и т.д. Таким образом, мы преобразуем новый файл в его старую версию, запуская каждый раз редактор <code>ed</code>.</p>
    <p>Очевидно, может возникнуть проблема, если в изменяемом файле есть строки, начинающиеся с трех символов. Кроме того, в разделе ошибок описания команды <code>diff(1)</code> (см. справочное руководство по UNIX) есть предупреждение о строках, состоящих из одной точки. Мы выбрали <code>@@@</code> для разделения команд редактирования, поскольку такая строка является редкостью для обычного текста.</p>
    <p>Конечно, было бы полезно показать здесь процесс развития команд <code>put</code> и <code>get</code>, но из-за ограниченного объема книги мы приведем только их окончательные варианты. Команда <code>put</code> проще команды <code>get</code>:</p>
    <p><code># put: install file into history</code></p>
    <empty-line/>
    <p><code>PATH=/bin:/usr/bin</code></p>
    <empty-line/>
    <p><code>case $# in</code></p>
    <p><code>1) HIST=$1.H ;;</code></p>
    <p><code>*) echo 'Usage: put file' 1&gt;&amp;2; exit 1 ;;</code></p>
    <p><code>esac</code></p>
    <p><code>if test ! -r $1</code></p>
    <p><code>then</code></p>
    <p><code> echo "put: can't open $1" 1&gt;&amp;2</code></p>
    <p><code> exit 1</code></p>
    <p><code>fi</code></p>
    <p><code>trap 'rm -f /tmp/put.[ab]$$; exit 1' 1 2 15</code></p>
    <p><code>echo -n 'Summary: '</code></p>
    <p><code>read Summary</code></p>
    <empty-line/>
    <p><code>if get -o /tmp/put.a$$ $1 # previous version</code></p>
    <p><code>then                      # merge pieces</code></p>
    <p><code> cp $1 /tmp/put.b$$       # current version</code></p>
    <p><code> echo"@@@ `getname` `date` $Summary" &gt;&gt;/tmp/put.b$$</code></p>
    <p><code> diff -e $1 /tmp/put.a$$ &gt;&gt;/tmp/put.b$$   # latest diffs</code></p>
    <p><code> sed -n '/^@@@/,$p' &lt;$HIST &gt;&gt;/tmp/put.b$$ # old diffs</code></p>
    <p><code> overwrite $HIST cat /tmp/put.b$$ # put it back</code></p>
    <p><code>else # make a new one</code></p>
    <p><code> echo "put: creating $HIST"</code></p>
    <p><code> cp $1 $HIST</code></p>
    <p><code> echo "@@@ `getname` `date` $Summary" &gt;&gt;$HIST</code></p>
    <p><code>fi</code></p>
    <p><code>rm -f /tmp/put.[ab]$$</code></p>
    <p>После считывания одной строки сводки команда <code>put</code> обращается к <code>get</code> для получения предыдущей версии файла из файла истории. Флаг <code>-о</code> команды <code>get</code> указывает на переключение выходного файла. В том случае, когда <code>get</code> не может найти файл истории, она возвращает код завершения ошибки, и <code>put</code> создает файл истории. Если файл истории существует, то в командах после <code>then</code> создается временный файл такого формата: самая последняя версия, строка <code>@@@</code>, команды редактора для преобразования этой версии в предыдущую, старые команды редактора и строки В конце временный файл копируется в файл истории с помощью команды <code>overwrite</code>.</p>
    <p>Команда <code>get</code> в отличие от <code>put</code> включает флаги:</p>
    <p><code># get: extract file from history</code></p>
    <empty-line/>
    <p><code>PATH=/bin:/usr/bin</code></p>
    <empty-line/>
    <p><code>VERSION=0</code></p>
    <p><code>while test "$1" != ""</code></p>
    <p><code>do</code></p>
    <p><code> case "$1" in</code></p>
    <p><code> -i) INPUT=$2; shift ;;</code></p>
    <p><code> -o) OUTPUT=$2; shift ;;</code></p>
    <p><code> -[0-9]) VERSION=$1 ;;</code></p>
    <p><code> -*) echo "get: Unknown argument $i" 1&gt;&amp;2; exit 1 ;;</code></p>
    <p><code> *) case "$OUTPUT" in</code></p>
    <p><code> "") OUTPUT=$1 ;;</code></p>
    <p><code> *) INPUT=$1.H ;;</code></p>
    <p><code> esac</code></p>
    <p><code> esac</code></p>
    <p><code> shift</code></p>
    <p><code>done</code></p>
    <p><code>OUTPUT=${OUTPUT?"Usage: get [-o outfile] [-i file.H] file"}</code></p>
    <p><code>INPUT=${INPUT-$OUTPUT.H}</code></p>
    <p><code>test -r $INPUT || { echo "get: no file $INPUT" 1&gt;&amp;2; exit 1; }</code></p>
    <p><code>trap 'rm -f /tmp/get.[ab]$$; exit 1' 1 2 15</code></p>
    <p><code># split into current version and editing commands</code></p>
    <p><code>sed &lt;$INPUT -n '1,/^@@@/w /tmp/get.a'$$'</code></p>
    <p><code>/^@@@/,$w /tmp/get.b'$$</code></p>
    <p><code># perform the edits</code></p>
    <p><code>awk &lt;/tmp/get.b$$ '</code></p>
    <p><code> <a l:href="">/^@@@/</a> { count++ }</code></p>
    <p><code> !/^@@@/ &amp;&amp; count &gt; 0 &amp;&amp; count &lt;= - "$VERSION"</code></p>
    <p><code> END { print "$d"; print "w", "'$OUTPUT'" }</code></p>
    <p><code>' | ed - /tmp/get.a$$</code></p>
    <p><code>rm -f /tmp/get.[ab]$$</code></p>
    <p>Флаги выполняют обычные функции: <code>-i</code> и <code>-о</code> задают переключение входного и выходного потоков, — <code>-[0-9]</code> определяет версию: <code>-0</code> — новая версия (значение по умолчанию), <code>-1</code> — предыдущая версия и т.д.). Цикл по аргументам организуется с помощью команд <code>while</code>, <code>test</code> и <code>shift</code>, а не с помощью <code>for</code>, поскольку некоторые флаги (<code>-i</code>, <code>-о</code>) используют еще один аргумент, и поэтому нужно сдвигать их командой <code>shift</code>, которая плохо согласуется с циклом <code>for</code>, если она находится внутри него. Флаг редактора <code>ed</code> отключает вывод числа символов, обычный при чтении и записи в файл.</p>
    <p>Строка</p>
    <p><code>test -r $INPUT || {echo "get: no file $INPUT" 1&gt;&amp;2; exit 1;}</code></p>
    <p>эквивалентна конструкции</p>
    <p><code>if test ! -r $INPUT</code></p>
    <p><code>then</code></p>
    <p><code> echo "get: no file $INPUT" 1&gt;&amp;2</code></p>
    <p><code> exit 1</code></p>
    <p><code>fi</code></p>
    <p>(такую конструкцию мы использовали в команде <code>put</code>), но запись ее короче, и она понятнее программистам, хорошо знакомым с операцией <code>||</code>. Команды, заключенные между <code>{</code> и <code>}</code>, выполняются не порожденным, а исходным интерпретатором. Это необходимо для того, чтобы команда <code>exit</code> обеспечивала выход из <code>get</code>, а не из порожденного интерпретатора. Символы <code>{</code> и <code>}</code> подобны <code>do</code> и <code>done</code> — они приобретают специальные значения, если следуют за точкой с запятой, символом перевода строки или другим символом завершения команды.</p>
    <p>В заключение мы рассмотрим те команды в <code>get</code>, которые и решают задачу. Вначале с помощью редактора <code>sed</code> файл истории разбивается на две части, содержащие самую последнюю версию и набор команд редактирования. Затем в <code>awk</code>-программе обрабатываются команды редактирования. Строки <code>@@@</code> подсчитываются (но не печатаются), и до тех пор, пока их число не превышает номера нужной версии, команды редактирования пропускаются (напомним, что действие, принятое по умолчанию, в <code>awk</code>-программе сводится к выводу входной строки). К командам редактирования из файла истории добавлены еще две команды <code>ed</code>: <code>$d</code> удаляет одну строку <code>@@@</code>, которую редактор <code>sed</code> оставил в текущей версии, а команда <code>w</code> помещает файл в отведенное ему место. Команда <code>overwrite</code> здесь не нужна, поскольку в <code>get</code> изменяется только версия файла, а не сам файл истории.</p>
    <cite>
     <subtitle>Упражнение 5.29</subtitle>
     <p>Напишите команду <code>version</code>, выполняющую два задания:</p>
     <p><code>$ version -5 файл</code></p>
     <p>выдает сводку изменений, дату изменения и имя пользователя, произведшего изменения выбранной из файла истории версии, а</p>
     <p><code>$ version sep 20 файл</code></p>
     <p>выдает номер версии, являющейся текущей 20 сентября. Типичное использование этой возможности ясно из обращения:</p>
     <p><code>$ get 'version sep 20 файл'</code></p>
     <p>(Команда <code>version</code> может для удобства создавать эхо имени файла истории.)</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.30</subtitle>
     <p>Измените команды <code>get</code> и <code>put</code> так, чтобы для работы с файлом истории они использовали отдельный каталог, а не загромождали текущий каталог файлами.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 5.31</subtitle>
     <p>Когда программа уже работает, не имеет смысла запоминать все версии. Как бы вы организовали исключение версий из середины файла истории?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>5.10 Заключение</p>
    </title>
    <p>Когда перед вами встает задача написать новую программу, возникает естественное желание сделать это на своем любимом языке программирования. Для нас таким языком чаще всего оказывается <code>shell</code>, хотя синтаксис его несколько необычен. <code>Shell</code> — удивительный язык программирования. Безусловно, это язык высокого уровня: операторами в нем являются целые программы. Поскольку он диалоговый, программы могут создаваться в диалоговом режиме и доводиться до рабочего состояния небольшими шагами. Далее, если они предназначены не только для личного пользования, их можно "вылизывать" и повышать надежность в расчете на широкий круг пользователей. В тех редких случаях, когда <code>shell</code>-программа оказывается неэффективной, часть ее или вся она может быть переписана на языке Си, но на основе уже проверенного алгоритма с работающей реализацией. (В следующей главе мы несколько раз пройдем этот путь.)</p>
    <p>Такой подход вообще характерен для программного мира UNIX — начинать работу не с нуля, а на базе того, что сделали другие, идти от простого к сложному, использовать программные средства для проверки новых идей.</p>
    <p>В настоящей главе мы привели много примеров, которые легко реализовать с помощью языка <code>shell</code> и существующих программ. Иногда достаточно лишь переопределить аргументы, как это было сделано в случае с командой <code>cal</code>. Иногда полезны циклы языка <code>shell</code> по последовательности имен файлов или наборам команд (см., например, <code>watchfor</code> или <code>checkmail</code>). Для более сложных вариантов все равно требуется меньше усилий, чем при программировании на Си. Так, наша версия команды <code>news</code> на языке <code>shell</code> заменяет программу на Си в 350 (!) строк.</p>
    <p>Однако дело не только в том, чтобы иметь возможность программировать на командном языке. Недостаточно иметь и множество программ. Проблема состоит в том, что все компоненты должны работать согласованно и придерживаться соглашений о представлении и передаче данных. Каждый компонент предназначен для решения одной задачи, причем наилучшим образом. Язык <code>shell</code> позволяет всякий раз, когда перед вами встает новая задача, связать различные программы воедино простым и эффективным способом. Именно интеграция — причина высокой продуктивности программного мира UNIX.</p>
    <cite>
     <subtitle>Историческая и библиографическая справка</subtitle>
     <p>Идеей использования команд <code>put</code> и <code>get</code> мы обязаны системе управления исходными текстами (Source Code Control System — SCCS), созданной M. Рочкиндом (The Source Code Control System. — IEEE Trans, on Software Engineering, 1975). Эта система более мощная и гибкая, чем наши простые программы; она предназначена для поддержания процесса создания больших программ. Однако основу SCCS составляет все та же программа <code>diff</code>.</p>
    </cite>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 6</p>
    <p>Программирование с помощью стандартных функций ввода-вывода</p>
   </title>
   <section>
    <p>До сих пор мы использовали существующие инструменты, чтобы разрабатывать новые, но сейчас уже достигнут разумный предел в создании новых средств с помощью <code>shell</code>, <code>sed</code> и <code>awk</code>. В этой главе нам предстоит написать простые программы на языке программирования Си. Основополагающая философия конструирования объектов, функционирующих совместно, будет по-прежнему оказывать влияние на построение программ, так как наша цель — подготовить инструменты, с которыми можно работать и на которые можно положиться. В каждом случае мы также попытаемся показать вам приемлемую стратегию реализации таких инструментов: начинать с минимума, обеспечивающего некоторые полезные свойства, а затем добавлять новые средства, если в них возникает необходимость.</p>
    <p>Существуют веские причины для того, чтобы писать новые программы "с нуля". Так, может оказаться, что проблема, с которой мы столкнулись, просто не может быть решена с помощью имеющихся программ. Часто приходится иметь дело с нетекстовыми файлами. Например, большинство программ, которые демонстрировались ранее, действительно хорошо работали лишь с текстовой информацией либо слишком трудно достигалась должная ясность или эффективность, если применялись только <code>shell</code> и другие средства общего назначения. В подобных случаях реализация с использованием <code>shell</code> может быть полезна для апробирования программы и ее интерфейса с пользователем. (Если же программа работает достаточно хорошо, нет причины для ее переделки.) Уже знакомая вам программа zap является в этом смысле неплохим примером: требуется всего несколько минут, чтобы написать первую версию на <code>shell</code>, которая имеет адекватный пользовательский интерфейс, но слишком медленна.</p>
    <p>Мы будем писать программы на языке Си — стандартном языке системы UNIX (ядро и все пользовательские программы написаны на Си), поскольку нет иного языка, хотя бы отчасти также хорошо поддерживаемого. Вы должны знать этот язык, по крайней мере в такой степени, чтобы свободно разбираться в предлагаемом здесь материале. Если это не так, прочтите книгу "Язык программирования Си" Б. Кернигана и Д. Ритчи (М.: Финансы и статистика, 1985)<a l:href="#n13" type="note">[13]</a>. Мы также воспользуемся "стандартной библиотекой ввода-вывода" — набором функций, обеспечивающих программы на Си эффективными и переносимыми средствами ввода-вывода и системными услугами. Стандартные библиотеки ввода-вывода есть во многих, отличных от UNIX, системах, поддерживающих Си, поэтому программы, взаимодействия с системой которых ограничены возможностями таких библиотек, могут быть легко перенесены.</p>
    <p>Примеры, подобранные в настоящей главе, имеют общее свойство: они представляют собой небольшие "инструменты", которые используются регулярно, но не являются частью седьмой версии системы. Если ваша система обладает подобными программами, вам будет легче сравнивать системы. Если же вы с ними еще не знакомы, то они могут оказаться вам чрезвычайно полезными. Эти программы помогут вам понять, что совершенной системы не существует, и для того чтобы добиться улучшения и устранить те или иные дефекты, достаточно приложить лишь небольшие усилия</p>
   </section>
   <section>
    <title>
     <p>6.1 Стандартные входной и выходной потоки: программа <code>vis</code></p>
    </title>
    <p>Многие программы читают только из одного входного потока и пишут в один выходной поток: для таких программ полностью подходят функции ввода-вывода, использующие лишь стандартные входной и выходной потоки, и для того чтобы начать работу, этого почти всегда достаточно.</p>
    <p>Проиллюстрируем изложенное с помощью программы <code>vis</code>, которая копирует свой стандартный входной поток в стандартный выходной, изображая при этом все непечатаемые символы в виде <code>\nnn</code>, где <code>nnn</code> — восьмеричное значение символа. <code>Vis</code> полезна для обнаружения "посторонних" или нежелательных символов, которые могут попасть в файлы. Например, <code>vis</code> будет печатать каждый символ "шаг назад" как \010, что является его восьмеричным значением:</p>
    <p><code>$ cat x abc</code></p>
    <p><code>$ vis &lt; x</code></p>
    <p><code>abc\010\010\010 ___ </code></p>
    <p><code>$</code></p>
    <p>Чтобы просмотреть несколько файлов с помощью этой элементарной версии <code>vis</code>, вы можете использовать <code>cat</code> для сбора файлов</p>
    <p><code>$ cat файл1 файл2 ... | vis</code></p>
    <p><code>...</code></p>
    <p><code>$ cat файл1 файл2 ... | vis | grep '\\'</code></p>
    <p><code>...</code></p>
    <p>и избежать тем самым выяснения способа доступа к файлам из программы.</p>
    <p>Между прочим, может показаться, что подобную работу следует выполнить с привлечением <code>sed</code>, поскольку команда <code>'1'</code> выдает на экран непечатаемые символы в наглядном виде:</p>
    <p><code>$ sed -n 1 x</code></p>
    <p><code>abc&#8592;&#8592;&#8592;___ </code></p>
    <p><code>$</code></p>
    <p>Результат выполнения программы <code>sed</code>, вероятно, вам покажется яснее, чем результат выполнения <code>vis</code>. Но применение <code>sed</code> к нетекстовым файлам бессмысленно:</p>
    <p><code>$ sed -n 1 /usr/you/bin</code></p>
    <p><code>$</code> Ничего в ответ!</p>
    <p>(Так получилось на PDP-11; в одной из систем для VAX <code>sed</code> аварийно завершилась, возможно, потому, что ввод был воспринят как очень длинная текстовая строка.) Таким образом, <code>sed</code> нам не подходит, и мы вынуждены писать новую программу.</p>
    <p>Простейшие функции ввода и вывода <code>getchar</code> и <code>putchar</code>. При каждом вызове <code>getchar</code> появляется очередной символ из стандартного входного потока, которому может быть поставлен в соответствие файл, конвейер или терминал (последнее принимается по умолчанию). Программа "не знает", что конкретно он собой представляет. Аналогично <code>putchar(c)</code> помещает символ в стандартный выходной поток, который по умолчанию также связан с терминалом.</p>
    <p>Функция <code>printf(3)</code> выполняет форматное преобразование при выводе. Вызовы <code>printf</code> и <code>putchar</code> могут следовать в любом порядке; выходной поток отразит порядок этих вызовов. Для форматного преобразования входного потока предусмотрена функция <code>scanf(3)</code>; она читает входной поток и разбивает его, как требуется, на строки, числа и т.п. Вызовы <code>scanf</code> и <code>getchar</code> также могут чередоваться.</p>
    <p>Приведем первую версию <code>vis</code>:</p>
    <p><code>/* vis: make funny characters visible (version 1) */</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#include &lt;ctype.h&gt;</code></p>
    <empty-line/>
    <p><code>main() {</code></p>
    <p><code> int c;</code></p>
    <empty-line/>
    <p><code> while ((c = getchar()) != EOF)</code></p>
    <p><code>  if (isascii(c) &amp;&amp;</code></p>
    <p><code>   (isprint(с) || c=='\n' || c=='\t' || c==' '))</code></p>
    <p><code>   putchar(c);</code></p>
    <p><code>  else</code></p>
    <p><code>   printf("\\%03o", c);</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <p><code>Getchar</code> возвращает из входного потока очередной байт или значение EOF, когда встречает конец файла (или ошибку). Между прочим, EOF не является байтом из файла; вспомните: во второй главе объяснялось, что такое "конец файла". Значение EOF отличается от значения любого байта, поэтому его трудно спутать с реальными данными; переменная с описана как <code>int</code> (целая), а; не как <code>char</code> (символьная), так что она может хранить значение EOF. Строка</p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p>должна находиться в начале каждого исходного файла. Это заставляет компилятор Си читать <emphasis>файл макроопределений</emphasis> (<code>/usr/include/stdio.h</code>), в котором специфицированы стандартные функции и имена, в том числе и <code>EOF</code>. Мы будем использовать <code>&lt;stdio.h&gt;</code> как краткую запись полного имени файла.</p>
    <p>Файл <code>&lt;ctype.h&gt;</code> — еще один файл макроопределений в <code>/usr/include</code>, который задает машинно-независимые макрокоманды (макросы) для классификации символов. Чтобы выяснить, принадлежит ли входной символ набору ASCII (т.е. его значение меньше 0200) и печатается ли он, мы использовали здесь <code>isascii</code> и <code>isprint</code>. Остальные макросы перечислены в табл. 6.1. Отметим, что <code>&lt;ctype.h&gt;</code> определяет символы "перевод строки", "табуляция" и пробел как непечатаемые.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>isalpha(c)</code></td>
      <td align="left" valign="top">Буква принадлежит алфавиту: <code>a-z A-Z</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>isupper(c)</code></td>
      <td align="left" valign="top">Прописная буква: <code>A-Z</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>islower(с)</code></td>
      <td align="left" valign="top">Строчная буква: <code>a-z</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>isdigit(c)</code></td>
      <td align="left" valign="top">Цифра: <code>0-9</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>isxdigit(c)</code></td>
      <td align="left" valign="top">Шестнадцатеричная цифра: <code>0-9 a-f A-F</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>isalnum(c)</code></td>
      <td align="left" valign="top">Буква или цифра</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>isspace(c)</code></td>
      <td align="left" valign="top">Пробел, символ табуляции, символ перевода строки, символ вертикальной табуляции, символ перевода страницы, символ возврата</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ispunct(c)</code></td>
      <td align="left" valign="top">Не буквенно-цифровой символ, не управляющий, не пробел</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>isprint(c)</code></td>
      <td align="left" valign="top">Печатаемый: любой графический символ</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>iscntrl(c)</code></td>
      <td align="left" valign="top">Управляющий символ: <code>0 &lt;= с &lt; 040 || с == 0177</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>isascii(c)</code></td>
      <td align="left" valign="top">Символ ASCII: <code>0 &lt;= с &lt;= 0177</code></td>
     </tr>
    </table>
    <p>Таблица 6.1: <strong>Макросы классификации символов <code>&lt;ctype.h&gt;</code></strong></p>
    <empty-line/>
    <p>Вызов <code>exit</code> в конце <code>vis</code> не является необходимым для корректной работы программы, но гарантирует тому, кто эту программу вызвал, получение нормального кода ее завершения (обычно нуля). Другой способ возврата кода завершения выполнить в теле функции <code>main</code> оператор <code>return 0</code>; возвращаемое значение <code>main</code> и есть код завершения программы. Если нет явно указанных <code>return</code> или <code>exit</code>, код завершения не определен.</p>
    <p>Для компиляции программы на Си поместите исходный текст в файл, имя которого оканчивается на <code>.с</code>, например <code>vis.с</code>, оттранслируйте его с помощью <code>сс</code> и запустите на выполнение результат, оставляемый компилятором в файле с именем <code>a.out</code> ('а' — ассемблер):</p>
    <p><code>$ сс vis.с</code></p>
    <p><code>$ a.out</code></p>
    <p><code>hello worldctl^g</code></p>
    <p><code>hello world\007</code></p>
    <p><code>ctl-d</code></p>
    <p><code>$</code></p>
    <p><code>a.out</code> можно переименовать после первого запуска или сделать это сразу с помощью флага <code>-о</code> команды <code>сс</code>:</p>
    <p><code>$ сс -о vis vis.с</code> Результат в vis, а не в a.out</p>
    <cite>
     <subtitle>Упражнение 6.1</subtitle>
     <p>Мы решили, что символы табуляции не следует делать видимыми, изображая их как <code>\011</code>, <code>&#8594;</code> или <code>\t</code>, поскольку главное назначение <code>vis</code> — поиск действительно аномальных символов. Можно принять альтернативное решение и недвусмысленно идентифицировать каждый символ в выходном потоке: символы табуляции, неграфические символы, пробелы в конце строки и т.п. Модифицируйте <code>vis</code> так, чтобы символы табуляции, обратная дробная черта, "шаг назад", перевод страницы и др. печатались в традиционном, принятом в Си представлении: <code>\t</code>, <code>\\</code>, <code>\b</code>, <code>\f</code> и т.д., причем пробелы в конце строки должны быть помечены. Можете сделать это недвусмысленным образом? Сравните ваш вариант с приведенным ниже:</p>
     <p><code>$ sed -n 1</code></p>
    </cite>
    <cite>
     <subtitle>Упражнение 6.2</subtitle>
     <p>Модифицируйте <code>vis</code> так, чтобы она приводила длинные строки к строкам некоторой разумной длины. Как это согласуется с требованием недвусмысленности результата из предыдущего упражнения?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>6.2 Аргументы программы: <code>vis</code> версия 2</p>
    </title>
    <p>Когда выполняется программа на Си, функции <code>main</code> передаются следующие аргументы из командной строки: счетчик <code>argc</code> и массив <code>argv</code>, состоящий из указателей символьных строк, содержащих аргументы. По соглашению <code>argv[0]</code> это имя самой команды, так что <code>argc</code> всегда больше нуля; "полезными" же являются аргументы <code>argv[1]</code>...<code>argv[argc - 1</code>]. Вспомните, что переключение входного или выходного потоков с помощью <code>&lt;</code> и <code>&gt;</code> осуществляется в <code>shell</code>, а не отдельными программами, поэтому такое переключение не влияет на число аргументов, видимых программой.</p>
    <p>Для иллюстрации работы с аргументами модифицируем <code>vis</code>, добавив флаг: <code>vis -s</code> удаляет любые непечатаемые символы вместо того, чтобы выделять их. Такое удаление удобно для "чистки" файлов из других систем, например тех, которые используют для завершения строки CRLF (символы возврата каретки и перевода строки) вместо одного символа перевода строки.</p>
    <p><code>/* vis: make funny characters visible (version 2) */</code></p>
    <empty-line/>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#include &lt;ctype.h&gt;</code></p>
    <empty-line/>
    <p><code>main(argc, argv)</code></p>
    <p><code> int argc;</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> int c, strip = 0;</code></p>
    <empty-line/>
    <p><code> if (argc &gt; 1 &amp;&amp; strcmp(argv[1], "-s") == 0)</code></p>
    <p><code>  strip = 1;</code></p>
    <p><code> while ((c = getchar()) != EOF)</code></p>
    <p><code>  if (isascii(c) &amp;&amp;</code></p>
    <p><code>   (isprint(с) || c=='\n' || c=='\t' || c==' '))</code></p>
    <p><code>   putchar(c);</code></p>
    <p><code>  else if (!strip)</code></p>
    <p><code>   printf("\\%03o", c);</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <p>Здесь <code>argv</code> — указатель массива, элементы которого служат указателями массивов символов; каждый такой массив заканчивается символом ASCII NUL (<code>'\0'</code>), поэтому массив можно считать строкой. Эта версия <code>vis</code> начинает свою работу с того, что проверяет, есть ли аргумент и является ли он <code>-s</code>. (Неверные аргументы игнорируются.) Функция <code>strcmp(3)</code> сравнивает две строки, возвращая нуль, если они одинаковы.</p>
    <p>В табл. 6.2 перечислены некоторые средства работы со строками и ряд полезных функций, одна из которых <code>strcmp</code>. Как правило, лучше воспользоваться этими стандартными функциями, чем писать собственные, так как они отлажены и зачастую выполняются быстрее, чем написанные вами, поскольку были оптимизированы для конкретных машин (нередко благодаря использованию Ассемблера).</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>strcat(s,t)</code></td>
      <td align="left" valign="top">Добавляет строку <code>t</code> к строке <code>s</code>; возвращает <code>s</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>strncat(s,t,n)</code></td>
      <td align="left" valign="top">Добавляет не более n символов <code>t</code> к <code>s</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>strcpy(s,t)</code></td>
      <td align="left" valign="top">Копирует <code>t</code> в <code>s</code>; возвращает <code>s</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>strncpy(s,t,n)</code></td>
      <td align="left" valign="top">Копирует точно n символов; при необходимости добавляет <code>NULL</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>strcmp(s,t)</code></td>
      <td align="left" valign="top">Сравнивает <code>s</code> и <code>t</code>, возвращает &lt;0, 0, &gt;0 при &lt;, ==, &gt;</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>strncmp(s,t,n)</code></td>
      <td align="left" valign="top">Сравнивает не более <code>n</code> символов</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>strlen(s)</code></td>
      <td align="left" valign="top">Возвращает длину <code>s</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>strchr(s,c)</code></td>
      <td align="left" valign="top">Возвращает указатель на первый символ с в <code>s</code> и <code>NULL</code>, если с отсутствует</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>strrchr(s,c)</code></td>
      <td align="left" valign="top">Возвращает указатель на последний <code>с</code> в <code>s</code> и <code>NULL</code>, если <code>с</code> отсутствует.</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>atoi(s)</code></td>
      <td align="left" valign="top">Возвращает целое значение <code>s</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>atof(s)</code></td>
      <td align="left" valign="top">Возвращает "плавающее" значение <code>s</code>; необходимо описание <code>double atof()</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>malloc(n)</code></td>
      <td align="left" valign="top">Возвращает указатель на область памяти в <code>n</code> байт и <code>NULL</code>, если это невозможно</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>calloc(n,m)</code></td>
      <td align="left" valign="top">Возвращает указатель на <code>n*m</code> обнуленных байтов и <code>NULL</code>, если это невозможно; <code>malloc</code> и <code>calloc</code> возвращают значение типа <code>char*</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>free(p)</code></td>
      <td align="left" valign="top">Освобождает память, выделенную <code>malloc</code> и <code>calloc</code></td>
     </tr>
    </table>
    <p>Таблица 6.2: <strong>Стандартные функции, выполняемые над строками</strong></p>
    <empty-line/>
    <cite>
     <subtitle>Упражнение 6.3</subtitle>
     <p>Измените аргумент <code>-s</code> так, чтобы <code>vis -sn</code> печатала только строки из <code>n</code> или более печатаемых символов, опуская непечатаемые символы и короткие последовательности обычных, печатаемых символов. Это полезно при выделении ''текстовых'' частей в нетекстовых файлах, таких, как рабочие программы. Некоторые версии системы содержат для подобных целей программу <code>strings</code>. Что лучше: иметь отдельную программу или пользоваться специальным аргументом <code>vis</code>?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 6.4</subtitle>
     <p>Доступность исходной программы на Си — одно из достоинств системы UNIX; такая программа демонстрирует элегантные решения многих программистских проблем. Прокомментируйте баланс между наглядностью программы на Си и встречающимися "оптимизированными" фрагментами, переписанными на Ассемблере.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>6.3 Доступ к файлам: <code>vis</code> версия 3</p>
    </title>
    <p>Две первые версии <code>vis</code> читают из стандартного входного потока и пишут в стандартный выходной поток, причем оба потока наследуются от <code>shell</code>. Следующий шаг модификация <code>vis</code> для работы с файлами по их именам, так что</p>
    <p><code>$ vis файл1 файл2 ...</code></p>
    <p>будет просматривать эти именованные файлы вместо стандартного входного потока. Если же имен файлов в качестве аргументов нет, <code>vis</code> должна читать стандартный входной поток.</p>
    <p>Возникает вопрос: как организовать чтение файлов, т.е. как связать имена файлов с операторами ввода вывода, реально читающими данные? Правила просты. Прежде чем быть прочитанным или записанным, файл должен быть <emphasis>открыт</emphasis> стандартной библиотечной функцией <code>fopen</code>. Последняя берет имя файла (например, <code>temp</code> или <code>/etc/passwd</code>), взаимодействует с ядром и возвращает обратно "внутреннее имя", которое используется при последующих операциях с данным файлом.</p>
    <p>Внутреннее имя является на самом деле указателем (называемым <emphasis>указателем файла</emphasis>) на структуру, содержащую информацию о файле, такую, как расположение буфера, текущую позицию символа в буфере, режим чтения или записи и т.п. Эта структура определяется в файле <code>&lt;stdio.h&gt;</code> и имеет имя <code>FILE</code>. Описание указателя файла таково:</p>
    <p><code>FILE *fp;</code></p>
    <p>Оно означает, что <code>fp</code> — указатель на <code>FILE</code>, <code>fopen</code> возвращает указатель на <code>FILE</code>; в <code>&lt;stdio.h&gt;</code> имеется описание типа для <code>fopen</code>. Реальный вызов функции <code>fopen</code>:</p>
    <p><code>char *name, *mode;</code></p>
    <p><code>fr = fopen(name, mode);</code></p>
    <p>Первый аргумент <code>fopen</code> представляет собой имя файла (строку символов). Второй аргумент также является символьной строкой, показывающей, как вы намереваетесь использовать файл; допустимые режимы: читать (<code>"r"</code>), писать (<code>"w"</code>) или дописать (<code>"а"</code>).</p>
    <p>Если файл, который вы открыли для записи или дописывания, не существует, он создается, если это возможно. Открытие для записи существующего файла вызывает уничтожение старого содержимого. Попытка читать несуществующий файл считается ошибкой, так же как и попытка читать или писать файл без разрешения. При возникновении ошибки <code>fopen</code> возвращает значение несуществующего указателя <code>NULL</code> (которое обычно определяется в <code>&lt;stdio.h&gt;</code> как <code>(char*)0</code>).</p>
    <p>Далее, нужен способ читать или писать файл после того, как он открыт. Есть несколько способов, из которых использование <code>getc</code> и <code>putc</code> самый простой. Функция <code>getc</code> выбирает из файла очередной символ:</p>
    <p><code>с = getc(fp)</code></p>
    <p>помещает в <code>с</code> следующий символ из файла, на который указывает <code>fp</code>. Эта функция возвращает <code>EOF</code> по достижении конца файла. Функция <code>putc</code> аналогична <code>getc</code>:</p>
    <p><code>putc(c, fp)</code></p>
    <p>помещает символ <code>с</code> в файл <code>fp</code> и возвращает <code>с</code>. Функции <code>getc</code> и <code>putc</code> возвращают <code>EOF</code> в случае ошибки.</p>
    <p>Когда программа начинает выполняться, уже открыты три файла и имеются их указатели. Это стандартные потоки: входной, выходной и поток диагностики; соответствующие указатели называются <code>stdin</code>, <code>stdout</code> и <code>stderr</code>. Указатели на файлы описаны в <code>&lt;stdio.h</code>&gt; и могут использоваться там, где может быть объект типа <code>FILE*</code>. Они являются не переменными, а константами, так что им нельзя присвоить значения. Вызов <code>getchar()</code> есть <code>getc(stdin)</code>, a <code>putchar(c)</code> есть <code>putc(c, stdout)</code>. На самом деле все эти четыре "функции" определены в <code>&lt;stdio.h&gt;</code> как макрокоманды. Они выполняются быстрее обычных вызовов функций ввиду отсутствия накладных расходов по вызову функции для каждого символа (см. табл. 6.3 с некоторыми другими определениями из <code>&lt;stdio.h&gt;</code>).</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>stdin</code></td>
      <td align="left" valign="top">Стандартный входной поток</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>stdout</code></td>
      <td align="left" valign="top">Стандартный выходной поток</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>stderr</code></td>
      <td align="left" valign="top">Стандартный поток диагностики</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>EOF</code></td>
      <td align="left" valign="top">Конец файла; обычно -1</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>NULL</code></td>
      <td align="left" valign="top">Несуществующий указатель; обычно 0</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>FILE</code></td>
      <td align="left" valign="top">Используется для описания указателей на файлы</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>BUFSIZ</code></td>
      <td align="left" valign="top">Обычно размер буфера ввода вывода (часто 512 или 1024)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>getc(fp)</code></td>
      <td align="left" valign="top">Возвращает один символ из потока <code>fp</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>getchar()</code></td>
      <td align="left" valign="top"><code>getc(stdin)</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>putc(c,fp)</code></td>
      <td align="left" valign="top">Помещает символ <code>с</code> в поток <code>fp</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>putchar(c)</code></td>
      <td align="left" valign="top"><code>putс(с,stdout)</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>feof(fp)</code></td>
      <td align="left" valign="top">Не нуль, если достигнут конец файла для потока <code>fp</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ferror(fp)</code></td>
      <td align="left" valign="top">Не нуль, если в потоке <code>fp</code> есть ошибка</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>fileno(fp)</code></td>
      <td align="left" valign="top">Дескриптор файла для потока <code>fp</code> (см. гл. 7)</td>
     </tr>
    </table>
    <p>Таблица 6.3: <strong>Некоторые определения из <code>&lt;stdio.h&gt;</code></strong></p>
    <empty-line/>
    <p>Теперь вернемся снова к нашей теме и напишем третью версию <code>vis</code>. Если есть аргументы командной строки, они обрабатываются в порядке очередности, если же аргументов нет, используется стандартный входной поток.</p>
    <p><code>/* vis: make funny characters visible (version 3) */</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#include &lt;ctype.h&gt;</code></p>
    <empty-line/>
    <p><code>int strip = 0; /* 1 =&gt; discard special characters */</code></p>
    <empty-line/>
    <p><code>main(argc, argv)</code></p>
    <p><code> int argc;</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> int i;</code></p>
    <p><code> FILE *fp;</code></p>
    <empty-line/>
    <p><code> while (argc &gt; 1 &amp;&amp; argv[1][0] == '-') {</code></p>
    <p><code>  switch (argv[1][1]) {</code></p>
    <p><code>  case 's': /* -s: strip funny chars */</code></p>
    <p><code>   strip = 1;</code></p>
    <p><code>   break;</code></p>
    <p><code>  default:</code></p>
    <p><code>   fprintf(stderr, "%s: unknown arg %s\n",</code></p>
    <p><code>    argv[0], argv[1]);</code></p>
    <p><code>   exit(1);</code></p>
    <p><code>  }</code></p>
    <p><code>  argc--;</code></p>
    <p><code>  argv++;</code></p>
    <p><code> }</code></p>
    <p><code> if (argc == 1)</code></p>
    <p><code>  vis(stdin);</code></p>
    <p><code> else</code></p>
    <p><code> for (i = 1; i &lt; argc; i++)</code></p>
    <p><code>  if ((fp=fopen(argv[i], "r")) == NULL) {</code></p>
    <p><code>   fprintf(stderr, "%s: can't open %s\n",</code></p>
    <p><code>    argv[0], argv[i]);</code></p>
    <p><code>   exit(1);</code></p>
    <p><code>  } else {</code></p>
    <p><code>   vis(fp);</code></p>
    <p><code>   fclose(fp);</code></p>
    <p><code>  }</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <p>В программе принято соглашение, по которому флаги стоят в начале списка аргументов. После обработки каждого флага <code>argv</code> и <code>argc</code> модифицируются так, что остальная часть программы не зависит от присутствия этого флага. Даже если <code>vis</code> распознает единственный флаг, мы написали программу в виде цикла, чтобы продемонстрировать единый способ обработки аргументов. В гл. 1 отмечалось, что программы UNIX обрабатывают флаги в произвольном порядке. Как одну из причин (помимо склонности к анархии) здесь можно назвать очевидную легкость написания программы разбора аргументов при любой модификации. Включение функции <code>getopt(3)</code> в некоторые системы является попыткой рационально объяснить ситуацию; вы можете ее исследовать, прежде чем писать собственную.</p>
    <p>Процедура <code>vis</code> выводит на печать единственный файл:</p>
    <p><code>vis(fp) /* make chars visible in FILE *fp */</code></p>
    <p><code> FILE *fp;</code></p>
    <p><code>{</code></p>
    <p><code> int c;</code></p>
    <empty-line/>
    <p><code> while ((с = getc(fp)) != EOF)</code></p>
    <p><code>  if (isascii(c) &amp;&amp;</code></p>
    <p><code>   (isprint(с) || c=='\n' || c=='\t' || c==' '))</code></p>
    <p><code>   putchar(c);</code></p>
    <p><code>  else if (!strip)</code></p>
    <p><code>   printf("\\%03o", c);</code></p>
    <p><code>}</code></p>
    <p>Функция <code>fprintf</code> идентична <code>printf</code>, за исключением аргумента указателя, специфицирующего файл, в который нужно писать.</p>
    <p>Функция <code>fclose</code> разрывает связь между указателем и внешним именем файла, установленную с помощью <code>fopen</code>, освобождая указатель для другого файла. Так как существует ограничение (около 20) на число файлов, которые одновременно могут быть открыты в программе, лучше всего закрывать уже не требующиеся вам файлы. Обычно выходной поток, выдаваемый любой стандартной библиотечной функцией, подобной <code>printf</code>, <code>putc</code> и т.д., для большей эффективности буферизуется так, чтобы его можно было писать большими фрагментами. (Исключение составляет выходной поток терминала, который, как правило, пишется по мере своего формирования или при печати символа перевода строки.) Применение <code>fclose</code> к выходному файлу инициирует выдачу последней буферизованной порции, <code>fclose</code> также вызывается автоматически для каждого открытого файла, когда программа выполняет <code>exit</code> или возвращается из <code>main</code>.</p>
    <p>Стандартный поток <code>stderr</code> присваивается программе тем же способом, что и <code>stdin</code> и <code>stdout</code>. Информация, записанная в <code>stderr</code>, оказывается на терминале пользователя даже при изменении назначения стандартного выходного потока. <code>Vis</code> пишет свою диагностику в <code>stderr</code> вместо <code>stdout</code>, так что если один из файлов по каким-то причинам недоступен, сообщение найдет путь на терминал пользователя, а не исчезнет в программном канале или в выходном файле. (Стандартный поток диагностики был изобретен позднее, чем программные каналы: после того, как сообщения об ошибках стали исчезать при передаче через эти каналы.)</p>
    <p>Мы решили, отчасти произвольно, что <code>vis</code> завершается, если не может открыть входной файл; это разумно для программы, чаще всего используемой в режиме диалога и с одним входным файлом. Однако вы можете предложить и другое решение.</p>
    <cite>
     <subtitle>Упражнение 6.5.</subtitle>
     <p>Напишите программу <code>printable</code>, которая печатает имя каждого аргумента-файла, содержащего только печатаемые символы; если в файле хранится любой непечатаемый символ, имя не печатается. printable полезна в ситуациях, подобных следующей:</p>
     <p><code>$ pr `printable *` | lpr</code></p>
     <p>Добавьте флаг <code>-v</code>, чтобы изменить смысл проверки на обратный, как в <code>grep</code>. Что следует делать, если среди аргументов нет имен файлов? Какой код завершения должна передавать <code>printable</code> при возврате?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>6.4 Вывод на экран порциями: программа <code>p</code></p>
    </title>
    <p>До сих пор мы использовали cat для просмотра файлов. Но если файл длинный, а связь с системой высокоскоростная, cat выдает выходной файл слишком быстро, что затрудняет его чтение, даже если вы успеваете делать это с помощью <emphasis>ctl-s</emphasis> и <emphasis>ctl-q</emphasis>.</p>
    <p>Очевидно, нужно иметь программу для печати файла небольшими удобными порциями, но такая программа не является стандартной, возможно, потому, что первоначальная система UNIX была написана в те времена, когда использовались терминалы "твердой копии" (печати на бумаге) и медленные линии связи. Поэтому наш следующий пример программа <code>p</code>, которая выводит файл по частям, занимающим полный экран, за один раз, ожидая ответа от пользователя после выдачи каждой порции прежде, чем начать очередной вывод (краткое имя <code>p</code> весьма удобно для программы, которая часто используется). Подобно другим программам, <code>p</code> читает или из файлов, поименованных, как аргументы, или из стандартного входного потока:</p>
    <p><code>$ p vis.с</code></p>
    <p><code>...</code></p>
    <p><code>$ grep '#define' *.[ch] | p</code></p>
    <p><code>...</code></p>
    <p><code>$</code></p>
    <p>Эту программу легче всего писать на Си; стандартные средства неудобны, когда происходит смешанный ввод из файла или конвейера и с терминала. Решение состоит в том, чтобы печатать входной поток небольшими порциями. Удобный размер порции 22 строки, что составляет немногим меньше, чем размер в 24 строки на большинстве видеотерминалов, и одну треть стандартной страницы в 66 строк. Простой способ подсказки пользователю не печатать последний символ перевода строки каждой порции. Курсор остановится на правом конце строки, а не на левой границе (новой строки). При нажатии клавиши <emphasis>RETURN</emphasis> выполняется перевод строки, и следующая строка появляется в нужном месте. Если пользователь печатает <emphasis>ctl-d</emphasis> или <emphasis>q</emphasis> в конце экрана, выполнение программы <code>p</code> заканчивается.</p>
    <p>Мы не станем принимать специальных мер для вывода длинных строк. Мы также не будем беспокоиться о множестве файлов: просто перейдем от одного к другому без примечаний. Следующая команда</p>
    <p><code>$ p имена файлов...</code></p>
    <p>по своему действию аналогична команде</p>
    <p><code>$ cat имена файлов... | p</code></p>
    <p>Если нужны имена файлов, их можно добавить циклом <code>for</code>:</p>
    <p><code>$ for i in имена файлов</code></p>
    <p><code>&gt; do</code></p>
    <p><code>&gt;  echo $i:</code></p>
    <p><code>&gt;  cat $i</code></p>
    <p><code>&gt; done | p</code></p>
    <p>В самом деле, у нас оказывается слишком много средств, которые мы можем внести в программу. Лучше сделать "неоснащенную" версию, а затем развивать ее, как подскажет опыт. Иными словами, необходимые средства это те, которые действительно вам нужны, а не те, которые, по нашему мнению, вы хотели бы иметь.</p>
    <p>Структура <code>p</code> аналогична структуре <code>vis</code>: основная процедура выполняет цикл по файлам, вызывая функцию <code>print</code>, выполняющуюся с каждым файлом:</p>
    <p><code>/* p: print input in chunks (version 1) */</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <empty-line/>
    <p><code>#define PAGESIZE 22</code></p>
    <p><code>char *progname; /* program name for error message */</code></p>
    <empty-line/>
    <p><code>main(argc, argv)</code></p>
    <p><code> int argc;</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> int i;</code></p>
    <p><code> FILE *fp, *efopen();</code></p>
    <empty-line/>
    <p><code> progname = argv[0];</code></p>
    <p><code> if (argc ==1)</code></p>
    <p><code>  print(stdin, PAGESIZE);</code></p>
    <p><code> else</code></p>
    <p><code>  for (i = 1; i &lt; argc; i++) {</code></p>
    <p><code>   fp = efopen(argv[i], "r");</code></p>
    <p><code>   print(fp, PAGESIZE);</code></p>
    <p><code>   fclose(fp);</code></p>
    <p><code>  }</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <p>Функция <code>efopen</code> реализует весьма общую операцию: пытается открыть файл. Если же это невозможно, она выводит на печать сообщение об ошибке, и ее выполнение завершается. Чтобы обеспечить выдачу сообщений об ошибках, идентифицирующих программу, в которой происходит (или произошла) ошибка, <code>efopen</code> ссылается на внешнюю строку <code>progname</code>, где содержится имя программы, устанавливаемое в <code>main</code>:</p>
    <p><code>FILE *efopen(file, mode) /* fopen file, die if can't */</code></p>
    <p><code> char *file, *mode;</code></p>
    <p><code>{</code></p>
    <p><code> FILE *fp, *fopen();</code></p>
    <p><code> extern char *progname;</code></p>
    <empty-line/>
    <p><code> if ((fp = fopen(file, mode)) != NULL)</code></p>
    <p><code>  return fp;</code></p>
    <p><code> fprintf(stderr, "%s: can't open file %s mode %s\n",</code></p>
    <p><code>  progname, file, mode);</code></p>
    <p><code> exit(1);</code></p>
    <p><code>}</code></p>
    <p>Мы испытали две версии программы <code>efopen</code>, прежде чем остановиться на данной. Одна из них должна была после печати сообщения завершиться, возвратив нулевой указатель, свидетельствующий о неудаче. Это позволяет вызвавшей программе продолжить свое выполнение или завершиться. Другая версия снабжала <code>efopen</code> третьим аргументом, указывающим, следует ли возвращаться после того, как файл открыть не удалось. Почти во всех наших примерах, однако, нет смысла продолжать работу, если файл недоступен, так что текущая версия <code>efopen</code> является для нас наилучшей.</p>
    <p>Непосредственное выполнение команды <code>p</code> осуществляется в <code>print</code>:</p>
    <p><code>print(fp, pagesize) /* print fp in pagesize chunks */</code></p>
    <p><code> FILE *fp;</code></p>
    <p><code> int pagesize;</code></p>
    <p><code>{</code></p>
    <p><code> static int lines = 0; /* number of lines so far */</code></p>
    <p><code> char buf[BUFSIZ];</code></p>
    <p><code> while (fgets(buf, sizeof buf, fp) != NULL)</code></p>
    <p><code>  if (++lines &lt; pagesize)</code></p>
    <p><code>   fputs(buf, stdout);</code></p>
    <p><code>  else {</code></p>
    <p><code>   buf[strlen(buf)-1] = '\0';</code></p>
    <p><code>   fputs(buf, stdout);</code></p>
    <p><code>   fflush(stdout);</code></p>
    <p><code>   ttyin();</code></p>
    <p><code>   lines = 0;</code></p>
    <p><code>  }</code></p>
    <p><code>}</code></p>
    <p>Мы использовали здесь <code>BUFSIZ</code>, который определен в <code>&lt;stdio.h&gt;</code> как размер буфера входного потока. Функция <code>fgets(buf, size, fp)</code> выбирает следующую строку входного потока из <code>fp</code> до символа перевода строки (включая его) в буфер и добавляет завершающий символ <code>\0</code>. Копируется на более <code>size - 1</code> символов. По достижении конца файла возвращается <code>NULL</code>. (Конструкция <code>fgets</code> оставляет желать лучшего: она возвращает <code>buf</code> вместо счетчика символов и, кроме того, выдает предупреждение о том, что входная строка была слишком длинной. Символы не потеряны, но вы должны взглянуть на <code>buf</code>, чтобы понять, что в самом деле случилось.)</p>
    <p>Функция <code>strlen</code> возвращает длину строки, поэтому мы можем отбросить завершающий символ перевода строки последней входной строки. После вызова <code>fputs(buf, fp)</code> строка <code>buf</code> записана в файл <code>fp</code>. При вызове <code>fflush</code> в конце страницы происходит вывод буферизованного выходного текста.</p>
    <p>Считывание ответа пользователя в конце каждой страницы возложено на функцию <code>ttyin</code>. Функция <code>ttyin</code> не может читать стандартный входной поток, тогда как <code>p</code> должна выполняться, даже если входной поток поступает из файла или конвейера. Чтобы справиться с этим, программа открывает файл <code>/dev/tty</code>, которому поставлен в соответствие пользовательский терминал при любом переключении стандартного входного потока. Приведенная ниже функция <code>ttyin</code> возвращает первую букву ответа, но здесь это свойство не используется.</p>
    <p><code>ttyin() /* process response from /dev/tty (version 1) */</code></p>
    <p><code>{</code></p>
    <p><code> char buf[BUFSIZ];</code></p>
    <p><code> FILE *efopen();</code></p>
    <p><code> static FILE *tty = NULL;</code></p>
    <empty-line/>
    <p><code> if (tty == NULL)</code></p>
    <p><code>  tty = efopen("/dev/tty", "r");</code></p>
    <p><code> if (fgets(buf, BUFSIZ, tty) == NULL || buf[0] == 'q')</code></p>
    <p><code>  exit(0);</code></p>
    <p><code> else /* ordinary line */</code></p>
    <p><code>  return buf[0];</code></p>
    <p><code>}</code></p>
    <p>Указатель на файл <code>devtty</code> описан как статический, так что его значение сохраняется от одного вызова <code>ttyin</code> до другого; файл <code>/dev/tty</code> открывается только при первом вызове.</p>
    <p>Очевидно, есть дополнительные средства, которые без особых усилий можно ввести в <code>p</code>, однако наша первая версия этой программы только печатает 22 строки и ждет следующей порции. Прошло немало времени, прежде чем в нее были добавлены другие средства, но в настоящее время ими мало кто пользуется. В частности, весьма простое дополнение ввод переменной <code>pagesize</code> для хранения числа строк на странице. Значение переменной можно установить из командной строки</p>
    <p><code>$ p -n...</code></p>
    <p>Она печатает порции по <code>n</code> строк. Для этого требуется лишь добавить несколько знакомых вам операторов в начале <code>main</code>:</p>
    <p><code>/* p: print input in chunks (version 2) */</code></p>
    <p><code>...</code></p>
    <p><code>int i, pagesize = PAGESIZE;</code></p>
    <p><code>progname = argv[0];</code></p>
    <p><code>if (argc &gt; 1 &amp;&amp; argv[1][0] == '-') {</code></p>
    <p><code> pagesize = atoi(&amp;argv[1][1]);</code></p>
    <p><code> argc--;</code></p>
    <p><code> argv++;</code></p>
    <p><code>}</code></p>
    <p>Функция <code>atoi</code> превращает строку символов в целое число (см. справочное руководство по <code>atoi(3)</code>).</p>
    <p>Еще одно средство временно остановить вывод на экран в конце каждой страницы, чтобы выполнить какую-либо иную команду. По аналогии с <code>ed</code> и многими другими программами, если пользователь печатает строку, начинающуюся восклицательным знаком, остальная часть строки воспринимается как команда и передается <code>shell</code> для выполнения. Данное средство также тривиально, поскольку для этой цели предусмотрена функция <code>system(3)</code>, речь о которой пойдет ниже. Модифицированная версия <code>ttyin</code> такова:</p>
    <p><code>ttyin() /* process response from /dev/tty (version 2) */</code></p>
    <p><code>{</code></p>
    <p><code> char buf[BUFSIZ];</code></p>
    <p><code> FILE *efopen();</code></p>
    <p><code> static FILE *tty = NULL;</code></p>
    <empty-line/>
    <p><code> if (tty == NULL)</code></p>
    <p><code>  tty = efopen("/dev/tty", "r");</code></p>
    <p><code> for (;;) {</code></p>
    <p><code>  if (fgets(buf,BUFSIZ,tty) == NULL || buf[0] == 'q')</code></p>
    <p><code>   exit(0);</code></p>
    <p><code>  else if (buf[0] == '!') {</code></p>
    <p><code>   system(buf+1); /* BUG here */</code></p>
    <p><code>   printf("!\n");</code></p>
    <p><code>  else /* ordinary line */</code></p>
    <p><code>   return buf[0];</code></p>
    <p><code> }</code></p>
    <p><code>}</code></p>
    <p>К сожалению, эта версия <code>ttyin</code> имеет серьезный недостаток. Команда, запущенная с помощью <code>system</code>, получает стандартный входной поток от <code>p</code>, так что если <code>p</code> читает из программного канала или файла, их входные потоки могут мешать друг другу:</p>
    <p><code>$ cat /etc/passwd | p -1</code></p>
    <p><code>root:3d.fHR5KoB.3s:0:l:S.User:/:!ed</code> Вызвать ed из p</p>
    <p><code>?</code> ed читает /etc/passwd</p>
    <p><code>!</code> … запутывается и завершается</p>
    <p>Для решения этой проблемы необходимо знать, как управлять процессами в UNIX, о чем речь пойдет в разд. 7.4. Пока же примите к сведению, что использование стандартной библиотечной функции <code>system</code> может создать неприятности, однако <code>ttyin</code> работает правильно, если компилируется с версией <code>system</code>, описанной в гл. 7.</p>
    <p>Итак, мы написали две программы <code>vis</code> и <code>p</code>, которые можно считать вариантами <code>cat</code> с некоторыми "украшениями". Может быть, им следует быть частью <code>cat</code>, доступной с помощью флагов <code>-v</code> и <code>-р</code>? Вопрос о том, писать ли новую программу или добавлять какие-то средства к старой, возникает всегда, как только у людей появляются новые идеи. Мы не можем со всей определенностью ответить на данный вопрос, но приведем здесь некоторые принципы, которые, возможно, вам помогут.</p>
    <p>Основной принцип состоит в том, что программе следует выполнять только свою основную работу. Если у нее появляется слишком много функций, она становится большой, медленной, ее трудно сопровождать и использовать. В самом деле, ряд свойств часто остается невостребованным, поскольку пользователи никак не могут запомнить флаги.</p>
    <p>Поэтому <code>cat</code> и <code>vis</code> совмещать не рекомендуется. Если <code>cat</code> просто копирует входной поток без изменений, то <code>vis</code> его трансформирует. Соединение их дает программу с двумя разными функциями. Это очевидно также для <code>cat</code> и <code>p</code>: <code>cat</code> предназначена для быстрого эффективного копирования страниц, <code>p</code> для их "перелистывания". Кроме того, <code>p</code> преобразует выходной поток. Каждый 22-й символ перевода строки пропускается. Три отдельные программы представляются в таком случае правильным решением.</p>
    <cite>
     <subtitle>Упражнение 6.6</subtitle>
     <p>Работает ли p нормально, если <code>pagesize</code> не является положительным?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 6.7</subtitle>
     <p>Что еще можно было бы сделать с <code>p</code>? Оцените и реализуйте (если оно вам подходит) свойство вновь выводить части ранее введенного текста. (Это дополнительное средство нам очень нравится.) Добавьте возможность выводить неполное содержимое экрана после каждой паузы, а также просматривать текст вперед или назад по строкам, задаваемым номером или содержимым.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 6.8</subtitle>
     <p>Используйте средства манипуляций файлами, встроенные в <code>exec</code> <code>shell</code> (см. справочное руководство по <code>sh(1)</code>), чтобы фиксировать обращения к <code>system</code> с терминала <code>ttyin</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 6.9</subtitle>
     <p>Если вы забыли определить источник ввода для <code>p</code>, то программа "молча" ожидает ввода с терминала. Стоит ли искать эту возможную ошибку? Если да, то как? Подсказка: <code>isatty(3)</code>.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>6.5 Пример: <code>pick</code></p>
    </title>
    <p>Версия <code>pick</code> из гл. 5, несомненно, увеличивает возможности <code>shell</code>. Версия на Си, приведенная ниже, в чем-то отличается от рассмотренной в гл. 5. Если эта версия имеет аргументы, то они обрабатываются так же, как и ранее, но если определен единственный аргумент <code>'-'</code>, <code>pick</code> обрабатывает свой стандартный входной поток.</p>
    <p>Почему бы в отсутствие аргументов просто не читать стандартный входной поток? Рассмотрим вторую версию команды <code>zap</code> из разд. 5.6:</p>
    <p><code>kill $SIG `pick\`ps-ag | egrep "$*"\` | awk '{print $1}'`</code></p>
    <p>Что происходит, если шаблон <code>egrep</code> ни с чем не совпадает? В этом случае <code>pick</code> не имеет аргументов и читает свой стандартный входной поток; команда <code>zap</code> терпит неудачу загадочным образом. Требование явного аргумента простой способ устранить неоднозначность, и соглашение о <code>'-'</code> в <code>cat</code> и других программах показывает, как его определить.</p>
    <p><code>/* pick: offer choice on each argument */</code></p>
    <empty-line/>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <empty-line/>
    <p><code>char *progname; /* program name for error message */</code></p>
    <empty-line/>
    <p><code>main(argc, argv)</code></p>
    <p><code> int argc;</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> int i;</code></p>
    <p><code> char buf[BUFSIZ];</code></p>
    <empty-line/>
    <p><code> progname = argv[0];</code></p>
    <p><code> if (argc == 2 &amp;&amp; strcmp(argv[1], "-") == 0) /* pick - */</code></p>
    <p><code>  while (fgets(buf, sizeof buf, stdin) != NULL) {</code></p>
    <p><code>   buf[strlen(buf)-1] = '\0'; /* drop newline */</code></p>
    <p><code>   pick(buf);</code></p>
    <p><code>  }</code></p>
    <p><code> else</code></p>
    <p><code>  for (i = 1; i &lt; argc; i++)</code></p>
    <p><code>   pick(argv[i]);</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>pick(s) /* offer choice of s */</code></p>
    <p><code> char *s;</code></p>
    <p><code>{</code></p>
    <p><code> fprintf(stderr, "%s? ", s);</code></p>
    <p><code> if (ttyin() == 'y')</code></p>
    <p><code>  printf("%s\n", s);</code></p>
    <p><code>}</code></p>
    <p>Версия <code>pick</code> предоставляет возможность диалогового выбора аргументов в одной программе. Это не только обеспечивает полезное средство, но и уменьшает потребность в "интерактивных" флагах для других команд.</p>
    <cite>
     <subtitle>Упражнение 6.10</subtitle>
     <p>Если есть <code>pick</code>, существует ли необходимость в <code>rm -i</code>?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>6.6 Об ошибках и отладке</p>
    </title>
    <p>Если вы писали программы ранее, вам знакомо понятие ошибки. Однако важно не только создавать программы, свободные от ошибок, но и заботиться о том, чтобы ваш проект был прост, тщательно реализован и сохранял свою "чистоту" в процессе модификации.</p>
    <p>В UNIX много инструментов, которые помогут вам находить ошибки, хотя ни один из них не является действительно первоклассным. Для того чтобы продемонстрировать их, нам нужна ошибка; все же программы в этой книге совершенны. Поэтому мы "создадим" типичную ошибку. Рассмотрим приведенную выше функцию <code>pick</code>, но на сей раз с ошибкой (заглядывать в первоначальный вариант нечестно):</p>
    <p><code>pick(s) /* offer choice of s */</code></p>
    <p><code> char *s;</code></p>
    <p><code>{</code></p>
    <p><code> fprintf("%s? ", s);</code></p>
    <p><code> if (ttyin() == 'y')</code></p>
    <p><code>  printf("%s\n", s);</code></p>
    <p><code>}</code></p>
    <p>Что произойдет, если мы откомпилируем и запустим ее?</p>
    <p><code>$ сс pick.с -о pick</code></p>
    <p><code>$ pick *.с                                  </code>Попробуем</p>
    <p><code>Ошибка при обращении к памяти - сделан дамп </code>Катастрофа!</p>
    <p><code>$</code></p>
    <p>Сообщение "Ошибка при обращении к памяти" свидетельствует о том, что ваша программа пыталась работать с недозволенной областью памяти. Обычно в таком случае указатель содержит неправильное значение. "Ошибка адресации шины" другое диагностическое сообщение со сходным значением, часто обусловленное просмотром бесконечной строки. "Сделан дамп памяти" означает, что ядро сохранило состояние вашей выполняемой программы в файле <code>core</code> текущего справочника. Вы также можете заставить программу сделать дамп памяти, напечатав <emphasis>ctl-\</emphasis>, если она выполняется как фоновая, или с помощью команды <code>kill -3</code>, если она основная.</p>
    <p>Существуют две программы <code>adb</code> и <code>sdb</code>, назначение которых разбираться в "посмертной выдаче". Подобно большинству отладчиков, они "хитроумны", сложны и без них трудно обойтись. Программа <code>adb</code> есть в седьмой версии системы, a <code>sdb</code> доступна в более поздних версиях.</p>
    <p>Из-за ограниченного объема книги мы лишь частично покажем вам применение каждой программы, а именно распечатаем содержимое стека, т.е. выведем функцию, выполнявшуюся при аварийном завершении программы, функцию, которая ее вызывала, и т.д. Первая функция, указанная в распечатке стека, это то место, где находилась программа, когда она была аварийно завершена.</p>
    <p>Чтобы получить распечатку стека с помощью <code>adb</code>, нужно ввести команду <emphasis>$C</emphasis>:</p>
    <p><code>$ adb pick core </code>Вызывает adb</p>
    <p><code>$C              </code>Запрос содержимого стека</p>
    <p><code>~_strout(0175722,011,0,011200)</code></p>
    <p><code> adjust: 0</code></p>
    <p><code> fillch: 060542</code></p>
    <p><code>__doprnt(0177345,0176176,011200)</code></p>
    <p><code>~fprintf(011200,0177345)</code></p>
    <p><code> iop:  01120</code></p>
    <p><code> fmt:  0177345</code></p>
    <p><code> args: 0</code></p>
    <p><code>~pick(0177345)</code></p>
    <p><code> s: 0177345</code></p>
    <p><code>~main(035,0177234)</code></p>
    <p><code> argc: 035</code></p>
    <p><code> argv: 0177234</code></p>
    <p><code> i:    01</code></p>
    <p><code> buf:  0</code></p>
    <p><code>ctl-d           </code>Завершение</p>
    <p><code>$</code></p>
    <p>Здесь речь идет о том, что <code>main</code> была вызвана из <code>pick</code>, которая вызвала <code>fprintf</code>, а она в свою очередь вызвала <code>__doprnt</code>, вызвавшую <code>_strout</code>. Так как <code>__doprnt</code> не упомянута где-либо в <code>pick.с</code>, ошибка должна быть где-то в <code>fprintf</code> или выше. (Строки после каждой функции в распечатке показывают значения локальных переменных. <code>$С</code> подавляет данную информацию так же, как сама <code>$С</code> делает это в некоторых версиях <code>adb</code>.) Попытаемся теперь сделать то же самое с помощью <code>sdb</code>:</p>
    <p><code>$ sdb pick core</code></p>
    <p><code>Предупреждение: 'a.out не компилируется с -g</code></p>
    <p><code>lseek: address 0xa64 </code>Функция, где программа аварийно завершилась</p>
    <p><code>*t                   </code>Запрос распечатки стека</p>
    <p><code>lseek()</code></p>
    <p><code>fprintf(6154,2147479154)</code></p>
    <p><code>pick(2147479154)</code></p>
    <p><code>main(30,2147478988,2147479112)</code></p>
    <p><code>*q                   </code>Выход</p>
    <p><code>$</code></p>
    <p>Информация размещена по-иному, но есть общая основа: <code>fprintf</code>. (Распечатка стека другая, так как это сделано на машине VAX-11/750, на которой стандартная библиотека ввода вывода реализована иначе.) И если мы взглянем на вызов <code>fprintf</code> в неправильной версии <code>pick</code>, то обнаружим некорректность:</p>
    <p><code>fprintf("%s?", s);</code></p>
    <p>Здесь нет <code>stderr</code>, так что строка формата используется как ссылка к <code>FILE</code>, и, конечно, получается хаос.</p>
    <p>Мы показали вам типичную ошибку, которая является скорее результатом просмотра, а не неправильного программирования. Искать подобные ошибки при вызове функции с неверными аргументами можно также с помощью верифицирующей программы для Си <code>lint(1)</code>. Эта программа рассматривает Си-программы с точки зрения наличия ошибок, аспектов переносимости и сомнительных конструкций. Если мы запустим <code>lint</code> с файлом <code>pick.с</code>, ошибка идентифицируется:</p>
    <p><code>$ lint pick.с</code></p>
    <p><code>...</code></p>
    <p><code>fprintf, arg. 1 несовместим "llib-lc"(69) :: "pick.c"(28)</code></p>
    <p><code>...</code></p>
    <p><code>$</code></p>
    <p>Это означает, что первый аргумент в стандартной библиотеке определен иначе, чем в строке 28 вашей программы. Таким образом дана точная информация о том, что неверно.</p>
    <p>Программа <code>lint</code>, с одной стороны, указывает на недостатки в вашей программе, а с другой выдает много не относящихся к делу сообщений, которые мы выше опустили, и нужен некоторый опыт, чтобы уметь разбираться, какие из них необходимы, а какие следует игнорировать. Однако имеет смысл постараться, так как <code>lint</code> помогает обнаружить некоторые ошибки, которые человек увидеть практически не может. После длительного редактирования всегда стоит запустить <code>lint</code> и убедиться в том, что каждое выдаваемое этой программой предупреждение вам понятно.</p>
   </section>
   <section>
    <title>
     <p>6.7 Пример: <code>zap</code></p>
    </title>
    <p>Программа <code>zap</code>, которая избирательно уничтожает процессы, отличается от той, что была представлена в виде файла <code>shell</code> в гл. 5. Главная проблема данной версии скорость. Она создает много процессов и поэтому работает медленно, что недопустимо для программы, уничтожающей процессы с ошибками. Если переписать <code>zap</code> на Си, ее быстродействие повысится. Мы, однако, снова воспользуемся <code>ps</code>, чтобы найти информацию о процессе. Это намного легче, чем выуживать информацию из ядра, и, кроме того, мы имеем переносимый вариант. Программа <code>zap</code> открывает программный канал, входной поток для которого берется из <code>ps</code>, и читает из него, как из файла. Функция <code>popen(3)</code> аналогична <code>fopen</code>, за исключением того, что первый аргумент является командой, а не именем файла. То же самое справедливо и для <code>pclose</code>, но здесь она нам не нужна.</p>
    <p><code>/* zap: interactive process killer */</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#include &lt;signal.h&gt;</code></p>
    <empty-line/>
    <p><code>char *progname; /* program name for error message */</code></p>
    <p><code>char *ps = "ps -ag"; /* system dependent */</code></p>
    <empty-line/>
    <p><code>main(argc, argv)</code></p>
    <p><code> int argc;</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> FILE *fin, *popen();</code></p>
    <p><code> char buf[BUFSIZ];</code></p>
    <p><code> int pid;</code></p>
    <empty-line/>
    <p><code> progname = argv[0];</code></p>
    <p><code> if ((fin = popen(ps, "r")) == NULL) {</code></p>
    <p><code>  fprintf(stderr, "%s: can't run %s\n", progname, ps);</code></p>
    <p><code>  exit(1);</code></p>
    <p><code> }</code></p>
    <p><code> fgets(buf, sizeof buf, fin); /* get header line */</code></p>
    <p><code> fprintf(stderr, "%s", buf);</code></p>
    <p><code> while (fgets(buf, sizeof buf, fin) != NULL)</code></p>
    <p><code>  if (argc == 1 || strindex(buf, argv[1]) &gt;= 0) {</code></p>
    <p><code>   buf[strlen(buf)-1] = '\0'; /* suppress \n */</code></p>
    <p><code>   fprintf(stderr, "%s? ", buf);</code></p>
    <p><code>   if (ttyin() == 'y') {</code></p>
    <p><code>    sscanf(buf, "%d", &amp;pid);</code></p>
    <p><code>    kill(pid, SIGKILL);</code></p>
    <p><code>   }</code></p>
    <p><code>  }</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <p>Мы писали программу, чтобы использовать <code>ps -ag</code> (этот флаг системно зависим), но если вы не являетесь привилегированным пользователем, то можете уничтожать лишь свои собственные процессы.</p>
    <p>Первый вызов <code>fgets</code> выбирает заголовок из <code>ps</code>; интересно выяснить, что случится, если попытаться уничтожить "процесс", соответствующий данному заголовку.</p>
    <p>Функция <code>sscanf</code> представляет собой член семейства <code>scanf(3)</code> для форматного преобразования входной строки. Она преобразует строку, а не файл. Вызов <code>kill</code> из системы посылает специальный сигнал процессу; сигнал <code>SIGKILL</code>, определенный в <code>&lt;signal.h&gt;</code>, не может быть перехвачен или проигнорирован. Вы можете вспомнить пятую главу, где его численное значение равно девяти, но лучше использовать символические константы из файлов макроопределений, чем включать в свои программы загадочные числа.</p>
    <p>Если аргументы отсутствуют, <code>zap</code> предоставляет каждую строку выходного потока <code>ps</code> как возможность для выбора. При наличии аргумента <code>zap</code> предлагает только те выходные строки <code>ps</code>, которые ему соответствуют. Функция <code>strindex(s1, s2)</code> проверяет, соответствует ли аргумент какой-либо части строки выходного потока <code>ps</code>, используя <code>strncmp</code> (см. табл. 6.2). Функция <code>strindex</code> возвращает позицию <code>s2</code> в <code>s1</code> или -1, если ее там нет.</p>
    <p><code>strindex(s, t) /* return index of t in s, -1 if none */</code></p>
    <p><code> char *s, *t;</code></p>
    <p><code>{</code></p>
    <p><code> int i, n;</code></p>
    <empty-line/>
    <p><code> n = strlen(t);</code></p>
    <p><code> for (i = 0; s[i] != '\0'; i++)</code></p>
    <p><code>  if (strncmp(s+i, t, n) == 0)</code></p>
    <p><code>   return i;</code></p>
    <p><code> return -1;</code></p>
    <p><code>}</code></p>
    <p>В табл. 6.4 представлены широко используемые функции из стандартной библиотеки ввода вывода.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>fp=fopen(s, mode)</code></td>
      <td align="left" valign="top">Открыть файл <code>s</code>; значения <code>mode "r"</code>, <code>"w"</code>, <code>"a"</code> соответствуют чтению, записи и добавлению (при ошибке возвращается NULL)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>c=gets(fp)</code></td>
      <td align="left" valign="top">Читать символ: <code>getchar()</code> это <code>getc(stdin)</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>putc(c, fp)</code></td>
      <td align="left" valign="top">Записать символ: <code>putchar(c)</code> это <code>putc(c, stdout)</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>ungetc(c, fp)</code></td>
      <td align="left" valign="top">Вернуть символ во входной файл <code>fp</code>; можно вернуть не более одного символа за раз</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>scanf(fmt, a1, ...)</code></td>
      <td align="left" valign="top">Читать символы из <code>stdin</code> в <code>a1</code>, <code>...</code> в соответствии с <code>fmt</code>. Каждый <code>a<emphasis>i</emphasis></code> должен быть указателем</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>fscanf(fp,...)</code></td>
      <td align="left" valign="top">Читать из файла <code>fp</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>sscanf(s,...)</code></td>
      <td align="left" valign="top">Читать из строки <code>s</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>printf(fmt, a1, ...)</code></td>
      <td align="left" valign="top">Форматировать <code>a1</code>, ... в соответствии с <code>fmt</code>; печатать в <code>stdout</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>fprintf(fp, ...)</code></td>
      <td align="left" valign="top">Печатать <code>...</code> в файл <code>fp</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>sprintf(s, ...)</code></td>
      <td align="left" valign="top">Печатать <code>...</code> в строку s</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>fqets(s, n, fp)</code></td>
      <td align="left" valign="top">Читать не более <code>n</code> символов в <code>s</code> из <code>fp</code> (возвращается <code>NULL</code> по концу файла)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>fputs(s, fp)</code></td>
      <td align="left" valign="top">Печатать строку <code>s</code> в файл <code>fp</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>fflush(fp)</code></td>
      <td align="left" valign="top">Занести буферизованные данные выходного потока в файл <code>fp</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>fclose(fp)</code></td>
      <td align="left" valign="top">Закрыть файл <code>fp</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>fp=popen(s, mode)</code></td>
      <td align="left" valign="top">Открыть программный канал для команды <code>s</code> (см. <code>fopen</code>)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>pclose(fp)</code></td>
      <td align="left" valign="top">Закрыть программный канал <code>fp</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>system(s)</code></td>
      <td align="left" valign="top">Запустить команду <code>s</code> и ждать ее окончания</td>
     </tr>
    </table>
    <p>Таблица 6.4: <strong>Полезные стандартные функции ввода-вывода</strong></p>
    <empty-line/>
    <cite>
     <subtitle>Упражнение 6.11</subtitle>
     <p>Модифицируйте <code>zap</code> так, чтобы можно было применять любое число аргументов. В настоящем виде <code>zap</code> высвечивает на экране строку, соответствующую выбранному варианту. Будет она делать это? Если нет, модифицируйте программу соответствующим образом. Подсказка: <code>getpid(2)</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 6.12</subtitle>
     <p>Постройте <code>fgrep(1)</code> на основе <code>strindex</code>. Сравните время работы при сложных поисках, например 10 слов на документ. Почему <code>fgrep</code> выполняется быстрее?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>6.8 Диалоговая программа сравнения файлов: <code>idiff</code></p>
    </title>
    <p>Поддерживать две чем-то отличающиеся версии файла, каждая из которых содержит часть нужного вам файла, довольно распространенная проблема. Зачастую она возникает в тех случаях, когда изменения вносятся независимо двумя разными людьми. Программа <code>diff</code> подскажет вам, чем различаются файлы, но вы не получите никакой помощи, если захотите выбрать какую-то информацию из одного файла, а какую-то из другого.</p>
    <p>В этом разделе мы напишем программу <code>idiff</code> (диалоговая <code>diff</code>), которая предоставляет пользователю каждую порцию выходного потока <code>diff</code> и предлагает ему возможность выбора фрагментов "от и до" или их редактирования. Программа <code>idiff</code> помещает выбранные фрагменты в соответствующем порядке в файл <code>idiff.out</code>. Допустим, даны такие два файла:</p>
    <p><code><strong>file1:            file2:</strong></code></p>
    <p><code>This is           This is</code></p>
    <p><code>a test            not a test</code></p>
    <p><code>of                of</code></p>
    <p><code>your              our</code></p>
    <p><code>skill             ability.</code></p>
    <p><code>and comprehension.</code></p>
    <p><code>diff</code> вырабатывает следующее:</p>
    <p><code>$ diff file1 file2</code></p>
    <p><code>2c2</code></p>
    <p><code>&lt; a test</code></p>
    <p><code>---</code></p>
    <p><code>&gt; not a test</code></p>
    <p><code>4,6c4,5</code></p>
    <p><code>&lt; your</code></p>
    <p><code>&lt; skill</code></p>
    <p><code>&lt; and comprehension.</code></p>
    <p><code>---</code></p>
    <p><code>&gt; our</code></p>
    <p><code>&gt; ability.</code></p>
    <p><code>$</code></p>
    <p>Диалог с <code>idiff</code> может выглядеть так:</p>
    <p><code>$ idiff file1 file2</code></p>
    <p><code>2c2</code> Первое различие</p>
    <p><code>&lt; a test</code></p>
    <p><code>---</code></p>
    <p><code>&gt; not a test</code></p>
    <p><code>? &gt;             </code>Пользователь выбрал вторую версию</p>
    <p><code>4,6с4,5         </code>Второе различие</p>
    <p><code>&lt; your</code></p>
    <p><code>&lt; skill</code></p>
    <p><code>&lt; and comprehension.</code></p>
    <p><code>---</code></p>
    <p><code>&gt; our</code></p>
    <p><code>&gt; ability.</code></p>
    <p><code>? &lt;             </code>Пользователь выбрал первую (&lt;) версию</p>
    <p><code>idiff output in file idiff.out</code></p>
    <p><code>$ cat idiff.out </code>Выходной поток направляется в этот файл</p>
    <p><code>This is</code></p>
    <p><code>not a test of</code></p>
    <p><code>your skill</code></p>
    <p><code>and comprehension.</code></p>
    <p><code>$</code></p>
    <p>Если вместо <code>&lt;</code> или <code>&gt;</code> выдан ответ <code>е</code>, <code>idiff</code> вызывает <code>ed</code> с двумя группами уже прочитанных строк. Если вторым был ответ <code>е</code>, буфер редактора выглядел бы следующим образом:</p>
    <p><code>your</code></p>
    <p><code>skill</code></p>
    <p><code>and comprehension.</code></p>
    <p><code>---</code></p>
    <p><code>our</code></p>
    <p><code>ability.</code></p>
    <p>Все, что пишется редактором обратно в файл, идет в окончательный выходной поток.</p>
    <p>И, наконец, любая команда может быть выполнена внутри <code>idiff</code> с помощью временного выхода посредством <emphasis>!cmd</emphasis>.</p>
    <p>Технически самая трудная часть работы <code>diff</code>, и она уже выполнена. Таким образом, в задачи <code>idiff</code> входит разбор выходного потока <code>diff</code>, открытие, закрытие, чтение и считывание соответствующих файлов в нужное время. Главная функция <code>idiff</code> поддерживает файлы и запускает процесс <code>diff</code>:</p>
    <p><code>/* idiff: interactive diff */</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#include &lt;ctype.h&gt;</code></p>
    <empty-line/>
    <p><code>char *progname;</code></p>
    <p><code>#define HUGE 10000 /* large number of lines */</code></p>
    <empty-line/>
    <p><code>main(argc, argv)</code></p>
    <p><code> int argc;</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> FILE *fin, *fout, *f1, *f2, *efopen();</code></p>
    <p><code> char buf[BUFSIZ], *mktemp();</code></p>
    <p><code> char *diffout = "idiff.XXXXXX";</code></p>
    <empty-line/>
    <p><code> progname = argv[0];</code></p>
    <p><code> if (argc != 3) {</code></p>
    <p><code>  fprintf(stderr, "Usage: idiff file1 file2\n");</code></p>
    <p><code>  exit(1);</code></p>
    <p><code> }</code></p>
    <p><code> f1 = efopen(argv[1], "r");</code></p>
    <p><code> f2 = efopen(argv[2], "r");</code></p>
    <p><code> fout = efopen("idiff.out", "w");</code></p>
    <p><code> mktemp(diffout);</code></p>
    <p><code> sprintf(buf,"diff %s %s &gt;%s", argv[1], argv[2], diffout);</code></p>
    <p><code> system(buf);</code></p>
    <p><code> fin = efopen(diffout, "r");</code></p>
    <p><code> idiff(f1, f2, fin, fout);</code></p>
    <p><code> unlink(diffout);</code></p>
    <p><code> printf("%s output in file idiff.out\n", progname);</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <p>Функция <code>mktemp(3)</code> создает файл, имя которого гарантированно отличается от имени любого существующего файла. <code>Mktemp</code> переписывает свой аргумент: шесть символов <code>X</code> заменяются идентификатором процесса и буквой. Системный вызов <code>unlink(2)</code> удаляет поименованный файл из файловой системы.</p>
    <p>Циклическая обработка изменений, о которых сообщает <code>diff</code>, выполняется функцией <code>idiff</code>. Основная идея достаточно проста: печатать порцию выходного потока <code>diff</code>, пропускать нежелательные данные в одном файле, а затем копировать требуемый вариант из другого файла. В программе есть много утомительных подробностей, так что она оказывается несколько больше, чем нам бы хотелось, но по частям ее довольно легко понять.</p>
    <p><code>idiff(f1, f2, fin, fout) /* process diffs */</code></p>
    <p><code> FILE *f1, *f2, *fin, *fout;</code></p>
    <p><code>{</code></p>
    <p><code> char *tempfile = "idiff.XXXXXX";</code></p>
    <p><code> char buf[BUFSIZ], buf2[BUFSIZ], *mktemp();</code></p>
    <p><code> FILE *ft, *efopen();</code></p>
    <p><code> int cmd, n, from1, to1, from2, to2, nf1, nf2;</code></p>
    <empty-line/>
    <p><code> mktemp(tempfile);</code></p>
    <p><code> nf1 = nf2 = 0;</code></p>
    <p><code> while (fgets(buf, sizeof buf, fin) != NULL) {</code></p>
    <p><code>  parse(buf, &amp;from1, ftto1, &amp;cmd, &amp;from2, &amp;to2);</code></p>
    <p><code>  n = to1-from1 + to2-from2 + 1; /* #lines from diff */</code></p>
    <p><code>  if (cmd == 'c')</code></p>
    <p><code>   n += 2;</code></p>
    <p><code>  else if (cmd == 'a')</code></p>
    <p><code>   from1++;</code></p>
    <p><code>  else if (cmd == 'd')</code></p>
    <p><code>   from2++;</code></p>
    <p><code>  printf("%s", buf);</code></p>
    <p><code>  while (n-- &gt; 0) {</code></p>
    <p><code>   fgets(buf, sizeof buf, fin);</code></p>
    <p><code>   printf("%s", buf);</code></p>
    <p><code>  }</code></p>
    <p><code>  do {</code></p>
    <p><code>   printf("? ");</code></p>
    <p><code>   fflush(stdout);</code></p>
    <p><code>   fgets(buf, sizeof buf, stdin);</code></p>
    <p><code>   switch (buf[0]) {</code></p>
    <p><code>   case '&gt;':</code></p>
    <p><code>    nskip(f1, to1-nf1);</code></p>
    <p><code>    ncopy(f2, to2-nf2, fout);</code></p>
    <p><code>    break;</code></p>
    <p><code>   case '&lt;':</code></p>
    <p><code>    nskip(f2, to2-nf2);</code></p>
    <p><code>    ncopy(f1, to1-nf1, fout);</code></p>
    <p><code>    break;</code></p>
    <p><code>   case 'e':</code></p>
    <p><code>    ncopy(f1, from1-1-nf1, fout);</code></p>
    <p><code>    nskip(f2, from2-1-nf2);</code></p>
    <p><code>    ft = efopen(tempfile, "w");</code></p>
    <p><code>    ncopy(f1, to1+1-from1, ft);</code></p>
    <p><code>    fprintf (ft, "---\n");</code></p>
    <p><code>    ncopy(f2, to2+1-from2, ft);</code></p>
    <p><code>    fclose(ft);</code></p>
    <p><code>    sprintf(buf2, "ed %s", tempfile);</code></p>
    <p><code>    system(buf2);</code></p>
    <p><code>    ft = efopen(tempfile, "r");</code></p>
    <p><code>    ncopy(ft, HUGE, fout);</code></p>
    <p><code>    fclose(ft);</code></p>
    <p><code>    break;</code></p>
    <p><code>  case '!':</code></p>
    <p><code>   system(buf+1);</code></p>
    <p><code>   printf("!\n");</code></p>
    <p><code>   break;</code></p>
    <p><code>  default:</code></p>
    <p><code>   printf("&lt; or &gt; or e or !\n");</code></p>
    <p><code>   break;</code></p>
    <p><code>  }</code></p>
    <p><code> } while (buf[0]!='&lt;' &amp;&amp; buf[0]!='&gt;' &amp;&amp; buf[0]!='e');</code></p>
    <p><code> nf1 = to1;</code></p>
    <p><code> nf2 = to2;</code></p>
    <p><code> ncopy(f1, HUGE, fout); /* can fail on very long files */</code></p>
    <p><code> unlink(tempfile);</code></p>
    <p><code>}</code></p>
    <p>Функция <code>parse</code> выполняет рутинную, но тонкую работу по разбору строк, выдаваемых <code>diff</code>, извлекая четыре номера строки и команду (одну из <code>а</code>, <code>с</code> или <code>d</code>). При этом <code>parse</code> немного усложняется, так как <code>diff</code> может выдать либо один номер строки, либо два с той или другой стороны буквы команды:</p>
    <p><code>parse(s, pfrom1, pto1, pcmd, pfrom2, pto2)</code></p>
    <p><code> char *s;</code></p>
    <p><code> int *pcmd, *pfrom1, *pto1, *pfrom2, *pto2;</code></p>
    <p><code>{</code></p>
    <p><code>#define a2i(p) while (isdigit(*s)) p = 10*(p) + *s++ - '0'</code></p>
    <empty-line/>
    <p><code> *pfrom1 = *pto1 = *pfrom2 = *pto2 = 0;</code></p>
    <p><code> a2i(*pfrom1);</code></p>
    <p><code> if (*s == ',') {</code></p>
    <p><code>  s++;</code></p>
    <p><code>  a2i(*pto1);</code></p>
    <p><code> } else</code></p>
    <p><code>  *pto1 = *pfrom1;</code></p>
    <p><code> *pcmd = *s++;</code></p>
    <p><code> a2i(*pfrom2);</code></p>
    <p><code> if (*s == ',') {</code></p>
    <p><code>  s++;</code></p>
    <p><code>  a2i(*pto2);</code></p>
    <p><code> } else</code></p>
    <p><code>  *pto2 = *pfrom2;</code></p>
    <p><code>}</code></p>
    <p>Макрокоманда <code>a2i</code> выполняет специальное преобразование из ASCII в целое в тех четырех местах, где она встречается.</p>
    <p>Функции <code>nskip</code> и <code>ncopy</code> пропускают или копируют указанное число строк из файла:</p>
    <p><code>nskip(fin, n) /* skip n lines of file fin */</code></p>
    <p><code> FILE *fin;</code></p>
    <p><code>{</code></p>
    <p><code> char buf[BUFSIZ];</code></p>
    <empty-line/>
    <p><code> while (n-- &gt; 0)</code></p>
    <p><code>  fgets(buf, sizeof buf, fin);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>ncopy(fin, n, fout) /* copy n lines from fin to fout */</code></p>
    <p><code> FILE *fin, *fout;</code></p>
    <p><code>{</code></p>
    <p><code> char buf[BUFSIZ];</code></p>
    <empty-line/>
    <p><code> while (n-- &gt; 0) {</code></p>
    <p><code>  if (fgets(buf, sizeof buf, fin) == NULL)</code></p>
    <p><code>   return;</code></p>
    <p><code>  fputs(buf, fout);</code></p>
    <p><code> }</code></p>
    <p><code>}</code></p>
    <p>Программа <code>idiff</code>, если ее прервать, оставляет несколько файлов, хранящихся в <code>/tmp</code>. В следующей главе мы покажем, как перехватывать прерывания, чтобы убрать временные файлы, подобные использованным здесь.</p>
    <p>Если критически подойти к <code>zap</code> и <code>idiff</code>, то оказывается, что самая трудная работа была уже кем-то сделана ранее. Эти программы только обеспечивают удобное взаимодействие с другой программой, которая обрабатывает нужную информацию. Всегда имеет смысл воспользоваться плодами чужих трудов это позволяет повысить эффективность своей работы.</p>
    <cite>
     <subtitle>Упражнение 6.13</subtitle>
     <p>Добавьте команду <emphasis>q</emphasis> к <code>idiff</code>: ответ <emphasis>q</emphasis> с автоматически выберет остаток от альтернатив <code>'&lt;'</code>; <emphasis>q &gt;</emphasis> возьмет все оставшееся от альтернатив <code>'&gt;'</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 6.14</subtitle>
     <p>Модифицируйте <code>idiff</code> так, чтобы некоторые аргументы <code>idiff</code> передавались к <code>diff</code>; <code>-b</code> и <code>-h</code> вероятные кандидаты. Выполните еще одну модификацию <code>idiff</code>, позволяющую определять другой редактор, как в команде</p>
     <p><code>$ idiff -е другой редактор file1 file2</code></p>
     <p>Как взаимодействуют эти две модификации?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 6.15</subtitle>
     <p>Измените <code>idiff</code>, чтобы использовать <code>popen</code> и <code>pclose</code> вместо временного файла для выходного потока <code>diff</code>. Как это скажется на сложности и скорости выполнения программы?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 6.16</subtitle>
     <p>Если один из аргументов <code>diff</code> -каталог, то в этом каталоге идет поиск файла с именем, заданным другим аргументом. Но если вы попробуете сделать то же самое с <code>idiff</code>, то она почему-то собьётся. Объясните, что в данном случае происходит, и исправьте дефект.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>6.9 Доступ к среде</p>
    </title>
    <p>Из Си-программы легко "добраться" до переменных в среде <code>shell</code>, что можно использовать для упрощения адаптации программы к окружению. Допустим, например, что размер экрана вашего терминала больше обычного (24-строкового). Чего вы сможете добиться, применив p и воспользовавшись преимуществами своего терминала? Необходимость определять размер экрана всякий раз, когда вы вводите <code>p</code>, надоедает:</p>
    <p><code>$ p -36...</code></p>
    <p>Вы могли бы всегда вставлять файл <code>shell</code> в свой <code>bin</code>:</p>
    <p><code>$ cat /usr/you/bin/p</code></p>
    <p><code>exec /usr/bin/p -36 $*</code></p>
    <p><code>$</code></p>
    <p>Третье решение модифицировать <code>p</code>, чтобы использовать те переменные среды, которые определяют свойства вашего терминала. Предположим, что вы определили переменную <code>PAGESIZE</code> в своем <code>.profile</code>:</p>
    <p><code>PAGESIZE=36</code></p>
    <p><code>export PAGESIZE</code></p>
    <p>Функция <code>getenv("var")</code> ищет в среде командную переменную var и возвращает ее значение как строку символов или <code>NULL</code>, если переменная не определена. При наличии <code>getenv</code> легко модифицировать p: достаточно лишь добавить пару описаний и вызов <code>getenv</code> к началу основной программы.</p>
    <p><code>/* p: print input in chunks (version 3) */</code></p>
    <p><code>...</code></p>
    <p><code>char *p, *getenv();</code></p>
    <empty-line/>
    <p><code>progname = argv[0];</code></p>
    <p><code>if ((p=getenv("PAGESIZE")) != NULL)</code></p>
    <p><code> pagesize = atoi(p);</code></p>
    <p><code>if (argc &gt; 1 &amp;&amp; argv[1][0] == '-') {</code></p>
    <p><code> pagesize = atoi(&amp;argv[1][1]);</code></p>
    <p><code> argc--;</code></p>
    <p><code> argv++;</code></p>
    <p><code>}</code></p>
    <p>Флаги обрабатываются вслед за переменной среды, так что любой явно заданный размер страницы в конце концов заменит неявно заданный.</p>
    <cite>
     <subtitle>Упражнение 6.17</subtitle>
     <p>Модифицируйте <code>idiff</code> так, чтобы она искала в среде имя редактора, который следует применить. Измените 2, 3 и т.д., чтобы использовать <code>PAGESIZE</code>.</p>
    </cite>
    <cite>
     <subtitle>Историческая и библиографическая справка</subtitle>
     <p>Стандартная библиотека ввода-вывода была разработана Д. Ритчи вслед за переносимой библиотекой ввода вывода М. Леска. Оба пакета имели целью предоставить пользователю стандартные средства, чтобы можно было переносить программы без изменений с UNIX в другие системы.</p>
     <p>Наша версия <code>p</code> основана на программе Г. Спенсера. Программа <code>adb</code> написана С. Борном, <code>sdb</code> Г. Катцефом, a <code>lint</code> С. Джонсоном.</p>
     <p>Программа <code>idiff</code> в общих чертах построена на базе программы, первоначально написанной Дж. Маранзано. Сама <code>diff</code> детище Д. МакИлроя и основана на алгоритме, созданном независимо Г. Стоуном и В. Хантом совместно с Т. Шиманским (Hunt J. W., Szymanski Т. G. "A fast algorithm for computing longest common subsequences." CACM, May 1977.) Алгоритм "diff" описан в работе M. Д. МакИлроя и Д. В. Ханта "An algorithm for differential file comparison" (Bell Labs Computing Science Technical Report 41, 1976). В заключение приведем слова МакИлроя: "Я опробовал три различных алгоритма, прежде чем выбрал окончательный вариант. По сути <code>diff</code> позволяет не только хорошо понять программу, но и пересматривать ее до тех пор, пока она не станет правильной".</p>
    </cite>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 7</p>
    <p>Системные вызовы в UNIX</p>
   </title>
   <section>
    <p>В настоящей главе мы рассмотрим самый низкий уровень взаимодействия с операционной системой UNIX системные вызовы. Они являются входами в ядро. Эти средства предоставляются операционной системой; все остальные средства построены на их основе.</p>
    <p>Материал главы охватывает несколько важных областей. К ним относится прежде всего система ввода-вывода, являющаяся основной для функций типа <code>fopen</code> и <code>putс</code>. Речь пойдет также о файловой системе, в частности о каталогах и индексных дескрипторах. Затем мы обсудим процессы, т.е. как запускать задачи из программы, после чего поговорим о сигналах и прерываниях: что происходит, когда вы нажимаете клавишу <emphasis>DELETE</emphasis>, и как такую ситуацию должным образом обработать в программе.</p>
    <p>Как и в гл. 6, во многих примерах приводятся полезные программы, не вошедшие в седьмую версию. Даже если они не помогут вам непосредственно, знакомство с ними может навести вас на мысль о сходных средствах, которые целесообразно создать и для вашей системы.</p>
    <p>Подробное описание системных вызовов вы можете найти во втором разделе справочного руководства по UNIX, так как здесь освещаются лишь основные вопросы.</p>
   </section>
   <section>
    <title>
     <p>7.1 Ввод-вывод низкого уровня</p>
    </title>
    <p>Низкий уровень ввода-вывода представляет собой непосредственный вход в операционную систему Ваши программы читают или пишут файлы порциями некоторого подходящего размера. Ядро буферизует данные порциями, которые подходят для периферийных устройств, и планирует операции для устройств так, чтобы их производительность была оптимальной для всех пользователей.</p>
    <subtitle>Дескрипторы файлов</subtitle>
    <p>Ввод и вывод обычно осуществляются посредством чтения или записи файлов, так как все периферийные устройства, и даже ваш терминал, представлены как файлы в файловой системе. Таким образом, единый интерфейс обеспечивает связь между программой и периферийными устройствами.</p>
    <p>В самом общем случае, прежде чем читать или писать файл, необходимо сообщить системе о вашем намерении <emphasis>открыть</emphasis> файл. Если вы собираетесь писать в файл, может быть, его необходимо и <emphasis>создать</emphasis>. Система проверяет ваше право сделать это (существует ли файл и есть ли у вас разрешение на доступ к нему?), и при положительном результате проверки возвращает неотрицательное целое, называемое <emphasis>дескриптором файла</emphasis>. Всякий раз, когда нужно выполнить ввод-вывод через файл, для его идентификации вместо имени используется дескриптор файла. Вся информация об открытом файле поддерживается системой. Ваша программа ссылается на файл только через дескриптор файла. Указатель на <code>FILE</code>, как отмечалось в гл. 6, является ссылкой на структуру, которая наряду с прочим содержит дескриптор файла: макрокоманда <code>fileno(fp)</code>, определенная в <code>&lt;stdio.h&gt;</code>, возвращает дескриптор файла.</p>
    <p>Для обеспечения удобства ввода и вывода на терминал предусмотрены специальные меры. Когда программа запускается из <code>shell</code>, она получает три открытых файла с дескрипторами 0, 1 и 2 стандартный входной поток, стандартный выходной поток и стандартный файл диагностики. Всем им по умолчанию поставлен в соответствие терминал, поэтому если программа читает только через дескриптор файла 0 и пишет через дескрипторы файлов 1 и 2, она может выполнять ввод-вывод без открывания файлов. Если же программа открывает любые другие файлы, они будут иметь дескрипторы 3, 4 и т.д.</p>
    <p>При переключении ввода-вывода на файлы или программные каналы (к ним или от них) <code>shell</code> изменяет назначение терминала по умолчанию для дескрипторов файлов 0 и 1. Обычно дескриптор файла 2 закрепляется за терминалом, так что сообщения об ошибках могут поступать на него. Использование символики <code>shell</code>, такой, как <code>2&gt;filename</code> и <code>2&gt;&amp;1</code>, вызовет переназначение файла, присвоенного по умолчанию, но при этом присвоение файлов меняется в <code>shell</code>, а не программой. (Программа сама может переназначить их впоследствии, если потребуется, что, правда, бывает редко.)</p>
    <subtitle>Файловый ввод-вывод: <code>read</code> и <code>write</code></subtitle>
    <p>Весь ввод и вывод обеспечиваются двумя системными вызовами, <code>read</code> и <code>write</code>, которые доступны в Си с помощью функций с теми же именами. Для обеих первый аргумент это дескриптор файла, второй массив байтов, который служит источником данных или назначением, а третий число байтов, которые следует передать.</p>
    <p><code>int fd, n, nread, nwritten;</code></p>
    <p><code>char buf[SIZE];</code></p>
    <empty-line/>
    <p><code>nread = read(fd, buf, n);</code></p>
    <p><code>nwritten = write(fd, buf, n);</code></p>
    <p>Каждый вызов возвращает число переданных байтов. При чтении возвращенное число может быть меньше, чем запрошенное, поскольку для чтения оставлено менее <code>n</code> байт. (Когда файлу поставлен в соответствие терминал, <code>read</code> обычно читает до следующей строки, что составляет меньшую часть запрошенного.) Возвращаемое значение 0 подразумевает конец файла, а значение -1 обозначает некоторую ошибку. При записи возвращаемое значение есть число действительно записанных байтов; если оно не равно числу байтов, которое предполагается записать, возникает ошибка. Несмотря на то, что число байтов, которые следует читать или писать, не ограничено, наиболее часто используются два значения: 1, что соответствует одному символу за одно обращение ("не буферизовано"), и размер блока на диске, как правило,- 512 или 1024 байта (такое значение имеет <code>BUFSIZ</code> в <code>&lt;stdio.h&gt;</code>). Для иллюстрации изложенного здесь приведена программа копирования входного потока в выходной. Так как входной и выходной потоки могут переключаться на любой файл или устройство, она действительно скопирует что-нибудь куда-либо: это "скелетная" реализация <code>cat</code>.</p>
    <p><code>/* cat: minimal version */</code></p>
    <p><code>#define SIZE 512 /* arbitrary */</code></p>
    <empty-line/>
    <p><code>main() {</code></p>
    <p><code> char buf[SIZE];</code></p>
    <p><code> int n;</code></p>
    <empty-line/>
    <p><code> while ((n = read(0, buf, sizeof buf)) &gt; 0)</code></p>
    <p><code>  write(1, buf, n);</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <p>Если размер файла не кратен числу <code>SIZE</code>, некоторый вызов <code>read</code> вернет меньшее число байтов, которые должны быть записаны с помощью <code>write</code>; следующий затем вызов <code>read</code> вернет нуль.</p>
    <p>Чтение и запись порциями, подходящими для диска, будут наиболее эффективными, но даже ввод-вывод по одному символу за раз осуществим для умеренных объемов буферизуются ядром. Дороже всего обходятся обращения к системе. Программа <code>ed</code>, например, использует однобайтовый способ, чтобы читать стандартный входной поток. Мы хронометрировали работу данной версии cat для файла в 54 000 байт при шести значениях <code>SIZE</code>:</p>
    <p>Время (пользователь+система, в сек.)</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Размер</th>
      <th align="left" valign="top">PDP-11/40</th>
      <th align="left" valign="top">VAX-11/750</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top">1</td>
      <td align="left" valign="top">271.0</td>
      <td align="left" valign="top">188.8</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">10</td>
      <td align="left" valign="top">29.9</td>
      <td align="left" valign="top">19.3</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">100</td>
      <td align="left" valign="top">3.8</td>
      <td align="left" valign="top">2.6</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">512</td>
      <td align="left" valign="top">1.3</td>
      <td align="left" valign="top">1.0</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">1024</td>
      <td align="left" valign="top">1.2</td>
      <td align="left" valign="top">0.6</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top">5120</td>
      <td align="left" valign="top">1.0</td>
      <td align="left" valign="top">0.6</td>
     </tr>
    </table>
    <p>Размер блока на диске для системы на PDP-11 составляет 512 байт и 1024 байта — для VAX.</p>
    <p>Доступ нескольких процессоров к одному и тому же файлу в одно и то же время является совершенно законным: в самом деле, один процесс может писать, в то время как другой читает. Это обескураживает, если не входит в ваши планы, но иногда оказывается полезным. Даже несмотря на то, что при одном обращении к функции <code>read</code> возвращается 0, который сигнализирует о конце файла, следующий вызов <code>read</code> обнаружит наличие некоторого количества байтов, если еще данные пишутся в файл. Это соображение лежит в основе программы <code>readslow</code>, которая продолжает читать свой входной поток вне зависимости от того, достигла она конца файла или нет. Программа <code>readslow</code> удобна для наблюдения за работой программы.</p>
    <p><code>$ slowprog &gt;temp &amp;</code></p>
    <p><code>5213</code> идентификатор процесса</p>
    <p><code>$ readslow &lt;temp | grep something</code></p>
    <p>Иными словами, медленная программа выполняет вывод в файл; <code>readslow</code>, возможно, совместно с некоторыми другими программами "наблюдает", как накапливаются данные.</p>
    <p>По составу <code>readslow</code> идентична cat, за исключением того, что она зацикливается, а не завершается, когда встречает конец входного потока. Программа <code>readslow</code> должна использовать ввод-вывод низкого уровня, так как стандартная библиотечная функция продолжает выдавать <code>EOF</code> после первого конца файла.</p>
    <p><code>/* readslow: keep reading, waiting for more */</code></p>
    <p><code>#define SIZE 512 /* arbitrary */</code></p>
    <empty-line/>
    <p><code>main() {</code></p>
    <p><code> char buf[SIZE];</code></p>
    <p><code> int n;</code></p>
    <empty-line/>
    <p><code> for (;;) {</code></p>
    <p><code>  while ((n = read(0, buf, sizeof buf)) &gt; 0)</code></p>
    <p><code>   write(1, buf, n);</code></p>
    <p><code>  sleep(10);</code></p>
    <p><code> }</code></p>
    <p><code>}</code></p>
    <p>Функция <code>sleep</code> заставляет программу остановиться на определенное число секунд (см. справочное руководство по <code>sleep(3)</code>). Мы не хотим, чтобы программа долго занималась поиском дополнительных данных, так как на это расходуется время центрального процессора. Таким образом, наша версия <code>readslow</code> копирует свой входной поток до конца файла, "спит" какое-то время, затем снова возобновляет работу. Если пока она была "в паузе", пришли еще данные, они будут прочитаны следующим <code>read</code>.</p>
    <cite>
     <subtitle>Упражнение 7.1</subtitle>
     <p>Добавьте <code>readslow</code> аргумент <code>n</code>, так что установленное по умолчанию время паузы может быть изменено на <code>n</code> секунд. Некоторые системы обеспечивают флаг <code>-f</code> ("навсегда") для <code>tail</code>, которая объединяет функции <code>tail</code> и <code>readslow</code>. Прокомментируйте этот вариант.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 7.2</subtitle>
     <p>Что происходит с <code>readslow</code>, если читаемый файл обрывается? Как бы вы исправили ситуацию? Подсказка: читайте о <code>fstat</code> в разд. 7.3.</p>
    </cite>
    <subtitle>Создание файла: <code>open</code>, <code>creat</code>, <code>close</code>, <code>unlink</code></subtitle>
    <p>Все стандартные файлы, кроме установленных по умолчанию, — входной, выходной и файл диагностики вы должны явно открыть для чтения или записи. Это можно сделать с помощью двух системных вызовов — открыть и создать<a l:href="#n14" type="note">[14]</a>.</p>
    <p>Функция <code>open</code> весьма похожа на fopen из предыдущей главы, за исключением того, что вместо указателя файла она возвращает дескриптор файла, имеющий тип <code>int</code>.</p>
    <p><code>char *name;</code></p>
    <p><code>int fd, rwmode;</code></p>
    <p><code>fd = open(name, rwmode);</code></p>
    <p>Как и для <code>fopen</code>, аргумент <code>name</code> есть символьная строка, содержащая имя файла. Аргумент вида доступа, однако, другой: <code>rwmode</code> равен 0 для чтения, 1 — для записи, 2 — в том случае, когда нужно открыть файл для чтения и записи. При вызове <code>open</code> возвращается -1, если возникает какая-либо ошибка; иначе возвращается корректный дескриптор файла.</p>
    <p>Попытка открыть несуществующий файл является ошибкой. Системный вызов <code>creat</code> позволяет создать новые файлы или переписать старые.</p>
    <p><code>int perms;</code></p>
    <p><code>fd = creat(name, perms);</code></p>
    <p>Вызов <code>creat</code> возвращает дескриптор файла, если можно создать файл name, и -1 в противном случае. Если файл не существует, <code>creat</code> создает его с <emphasis>правами доступа</emphasis>, определяемыми аргументом <code>perms</code>. Существующий файл <code>creat</code> сокращает до нулевой длины, т.е. применение <code>creat</code> к уже существующему файлу не является ошибкой (права доступа при этом не изменяются). Безотносительно к правам доступа файл, к которому было обращение <code>creat</code>, открыт для записи.</p>
    <p>Как известно из второй главы, с файлом связаны девять битов информации о защите, контролирующих разрешение на чтение, запись или выполнение, так что число из трех восьмеричных цифр удобно для спецификации этой информации. Например, 0755 дает разрешение владельцу файла читать, писать и выполнять его, а чтение и выполнение файла доступно любому пользователю. Не забывайте о первом нуле, который определяет восьмеричные числа в языке Си.</p>
    <p>Иллюстрацией изложенного может служить упрощенная версия <code>cp</code>. Ее главный недостаток состоит в том, что она копирует только один файл и не разрешает использовать в качестве второго аргумента каталог. Кроме того, наша версия не сохраняет права доступа файла-источника; в дальнейшем мы покажем, как это исправить.</p>
    <p><code>/* cp: minimal version */</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#define PERMS 0644 /* RW for owner, R for group, others */</code></p>
    <empty-line/>
    <p><code>char *progname;</code></p>
    <empty-line/>
    <p><code>main(argc, argv) /* cp: copy f1 to f2 */</code></p>
    <p><code> int argc;</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> int f1, f2, n;</code></p>
    <p><code> char buf[BUFSIZ];</code></p>
    <empty-line/>
    <p><code> progname = argv[0];</code></p>
    <p><code> if (argc != 3)</code></p>
    <p><code>  error("Usage: %s from to", progname);</code></p>
    <p><code> if ((f1 = open(argv[1], 0)) == -1)</code></p>
    <p><code>  error("can't open %s", argv[1]);</code></p>
    <p><code> if ((f2 = creat(argv[2], PERMS)) == -1)</code></p>
    <p><code>  error("can't create %s", argv[2]);</code></p>
    <p><code> while ((n = read(f1, buf, BUFSIZ)) &gt; 0)</code></p>
    <p><code>  if (write(f2, buf, n) != n)</code></p>
    <p><code> error("write error", (char*)0);</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <p><code>error</code> мы обсудим ниже.</p>
    <p>Число файлов, которые одновременно могут быть открыты программой, ограничено (обычно порядка 20; см. <code>NOFILE</code> в <code>&lt;SYS/param.h&gt;</code>). Поэтому любая программа, которой предстоит обрабатывать много файлов, должна быть готова неоднократно использовать одни и те же дескрипторы файлов. Системный вызов <code>close</code> разрывает связь между именем и дескриптором файла, освобождая дескриптор для использования с некоторым другим файлом. Завершение программы посредством <code>exit</code> и возврат из основной программы закрывают все открытые файлы. Вызов системы unlink удаляет файл из файловой системы.</p>
    <subtitle>Обработка ошибок: <code>errno</code></subtitle>
    <p>Обсуждаемые здесь системные вызовы, а по сути все системные вызовы, могут вызывать ошибки. Обычно они сигнализируют об ошибке, возвращая значение -1. Иногда полезно знать, какая именно ошибка произошла, поэтому системные вызовы, когда это приемлемо, оставляют номер ошибки во внешней целой переменной, называемой <code>errno</code>. (Значение различных номеров ошибок объясняется во введении к разд. 2 справочного руководства по UNIX.) С помощью <code>errno</code> ваша программа может определить, например, чем вызвана неудача при открытии файла — тем, что он не существует, или тем, что у вас нет разрешения на его чтение. Кроме того, есть массив символьных строк <code>sys_errlist</code>, индексируемый <code>errno</code>, который переводит число в строку, передающую смысл ошибки. Наша версия error использует эти структуры данных:</p>
    <p><code>error(s1, s2) /* print error message and die */</code></p>
    <p><code> char *s1, *s2;</code></p>
    <p><code>{</code></p>
    <p><code> extern int errno, sys_nerr;</code></p>
    <p><code> extern char *sys_errlist[], *progname;</code></p>
    <empty-line/>
    <p><code> if (progname)</code></p>
    <p><code>  fprintf(stderr, "%s: ", progname);</code></p>
    <p><code> fprintf(stderr, s1, s2);</code></p>
    <p><code> if (errno &gt; 0 &amp;&amp; errno &lt; sys_nerr)</code></p>
    <p><code>  fprintf (stderr, " (%s)", sys_errlist[errno]);</code></p>
    <p><code> fprintf(stderr, "\n");</code></p>
    <p><code> exit(1);</code></p>
    <p><code>}</code></p>
    <p><code>Errno</code> первоначально равна нулю и всегда должна быть меньше, чем <code>sys_herr.</code> Она не становится нулевой вновь при нормальной работе, поэтому вы должны обнулять ее после каждой ошибки, если ваша программа будет продолжать выполняться. Сообщения об ошибках в нашей версии <code>cp</code> появляются следующим образом:</p>
    <p><code>$ cp foo bar</code></p>
    <p><code>cp: can't open foo       </code>(Нет такого файла или каталога)</p>
    <p><code>$ date &gt;foo; chmod 0 foo </code>Создать нечитаемый файл</p>
    <p><code>$ cp too bar</code></p>
    <p><code>cp: can't open foo       </code>(В разрешении отказано)</p>
    <p><code>$</code></p>
    <subtitle>Произвольный доступ: <code>lseek</code></subtitle>
    <p>Файл ввода-вывода обычно последовательный: каждый <code>read</code> или <code>write</code> занимает место в файле непосредственно после использованного при предыдущем вызове. Однако при необходимости файл может быть прочитан или записан в произвольном порядке. Системный вызов <code>lseek</code> позволяет перемещаться по файлу, не осуществляя ни чтения, ни записи:</p>
    <p><code>int fd, origin;</code></p>
    <p><code>long offset, pos, lseek();</code></p>
    <empty-line/>
    <p><code>pos = lseek(fd, offset, origin);</code></p>
    <p>Текущая позиция в файле с дескриптором <code>fd</code> перемещается к позиции <code>offset</code>, которая отсчитывается относительно места, определяемого <code>origin</code>. Последующие процессы чтения или записи будут начинаться с этой позиции. <code>Origin</code> может иметь значения 0, 1, 2, задавая тем самым начало отсчета значения <code>offset</code> — от начала, от текущей позиции или от конца файла соответственно.</p>
    <p>Возвращаемое значение есть новая абсолютная позиция или -1 при ошибке. Например, при добавлении информации в файл нужно дойти до его конца, а затем выполнить запись:</p>
    <p><code>lseek(fd, 0L, 2);</code></p>
    <p>Чтобы вернуться обратно к началу ("перемотать"), необходимо вызвать</p>
    <p><code>lseek(fd, 0L, 0);</code></p>
    <p>Для определения текущей позиции следует выполнить</p>
    <p><code>pos = lseek(fd, 0L, 1);</code></p>
    <p>Обратите внимание на аргумент <code>0L</code>: смещение есть длинное целое. ('l' в lseek означает 'long' — длинный, чтобы отличить его от системного вызова <code>seek</code> в шестой версии, где используются короткие целые.)</p>
    <p>С помощью <code>lseek</code> можно обращаться с файлами как с большими массивами, однако при этом время доступа к ним возрастает. Например, следующая функция читает любое число байтов из любого места в файле:</p>
    <p><code>get(fd, pos, buf, n) /* read n bytes from position pos */</code></p>
    <p><code> int fd, n;</code></p>
    <p><code> long pos;</code></p>
    <p><code> char *buf;</code></p>
    <p><code>{</code></p>
    <p><code> if (lseek(fd, pos, 0) == -1) /* get to pos */</code></p>
    <p><code>  return -1;</code></p>
    <p><code> return read(fd, buf, n);</code></p>
    <p><code>}</code></p>
    <cite>
     <subtitle>Упражнение 7.3</subtitle>
     <p>Модифицируйте <code>readslow</code> так, чтобы обрабатывать имя файла в качестве аргумента, если оно присутствует. Добавьте <code>-е</code>:</p>
     <p><code>$ readslow -е</code></p>
     <p>заставляет <code>readslow</code> искать конец входного потока, прежде чем начать чтение. Каковы функции <code>lseek</code> при работе с программным каналом?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 7.4</subtitle>
     <p>Перепишите <code>efopen</code> из гл. 6, чтобы вызвать error.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>7.2 Файловая система: каталоги</p>
    </title>
    <p>Наша следующая тема — как ориентироваться в иерархии каталогов. При этом мы будем использовать не новые системные вызовы, а лишь несколько старых в новом контексте. В качестве примера приведем функцию <code>spname</code>, которая пытается справиться с неверно написанными именами файлов. Функция</p>
    <p><code>n = spname(name, newname);</code></p>
    <p>ищет файл с именем, "достаточно близким" к name. Если такое имя найдено, оно копируется в <code>newname</code>. Значение n, возвращаемое <code>spname</code>, равно -1, если ничего достаточно близкого не найдено, 0 — при точном совпадении и 1, если была сделана коррекция.</p>
    <p><code>Spname</code> является удобным дополнением к команде p: если вы пытаетесь печатать файл, но неверно написали имя, <code>p</code> спросит вас, не имели ли вы в виду что-либо другое:</p>
    <p><code>$ p /urs/srx/ccmd/p/spnam.с  </code>Очень плохое имя</p>
    <p><code>"/usr/src/cmd/p/spname.с"? y </code>Предложенная коррекция принята</p>
    <empty-line/>
    <p><code>/* spname: возвращает верно написанное имя файла */</code></p>
    <p><code>...</code></p>
    <p>Пока мы пишем имя файла, <code>spname</code> пытается исправить каждую его составную часть, в которой несовпавшая буква была опущена, оказалась лишней, просто неверна или поменялась местами с другой буквой. Это удобное средство рассчитано на того, кто печатает не очень внимательно.</p>
    <p>Прежде чем писать программу, уместно сделать короткий обзор структуры файловой системы. Каталог представляет собой файл, содержащий список имен файлов и указание, где они размещены. Место размещения определяется индексом в так называемой <emphasis>индексной таблице файлов</emphasis>. В записи индексной таблицы содержится вся информация о файле, кроме его имени. Строка каталога, таким образом, состоит из двух элементов — индекса файла и его имени. Точное описание можно найти в файле <code>&lt;sys/dir.h&gt;</code>:</p>
    <p><code>$ cat /usr/include/sys/dir.h</code></p>
    <p><code>#define DIRSIZ 14 /* максимальная длина имени файла */</code></p>
    <empty-line/>
    <p><code>struct direct /* структура строки каталога */</code></p>
    <p><code>{</code></p>
    <p><code> ino_t d_ino; /* номер индексного дескриптора */</code></p>
    <p><code> char d_name[DIRSIZ]; /* имя файла */</code></p>
    <p><code>};</code></p>
    <p><code>$</code></p>
    <p>"Тип" <code>ino_t</code> это <code>typedef</code>, описывающий индекс в индексной таблице. Он является коротким целым без знака (<code>unsigned short</code>) в версиях системы для PDP-11 и VAX и не должен включаться в программу, так как может быть иным на другой машине. Поэтому мы воспользуемся определением типа <code>typedef</code>. Полный набор "системных" типов находится в <code>&lt;sys/types.h&gt;</code>, который должен быть включен до <code>&lt;sys/dir.h&gt;</code>.</p>
    <p>Действия <code>spname</code> достаточно прямолинейны, хотя и требуют выполнения нескольких граничных условий. Предположим, что имя файла <code>/d1/d2/f</code>. Основная идея состоит в следующем: отделить первую компоненту (<code>/</code>), найти в каталоге имя, близкое к следующей компоненте (<code>d1</code>), затем найти имя, близкое к <code>d2</code>, и т.д. до тех пор, пока не будет достигнуто полное совпадение для каждой составной части. Если на какой-то стадии в каталоге не окажется подходящего кандидата, поиск прекратится.</p>
    <p>Мы разбили процесс на три функции. Сама <code>spname</code> выделяет компоненты пути и составляет из них имя файла, наилучшим образом совпадающее с исходным. Функция <code>mindist</code> ищет в данном каталоге файл с именем, ближайшим к составленному функцией <code>spname</code>. Функция <code>spdist</code> вычисляет "расстояние" между двумя именами.</p>
    <p><code>/* spname: return correctly spelled filename */</code></p>
    <p><code>/*</code></p>
    <p><code> * spname(oldname, newname) char *oldname, *newname;</code></p>
    <p><code> * returns -1 if no reasonable match to oldname,</code></p>
    <p><code> * 0 if exact match,</code></p>
    <p><code> *1 if corrected.</code></p>
    <p><code> * stores corrected name in newname.</code></p>
    <p><code> */</code></p>
    <p><code>#include &lt;sys/types.h&gt;</code></p>
    <p><code>#include &lt;sys/dir.h&gt;</code></p>
    <empty-line/>
    <p><code>spname(oldname, newname)</code></p>
    <p><code> char *oldname, *newname;</code></p>
    <p><code>{</code></p>
    <p><code> char *p, guess[DIRSIZ+1], best[DIRSIZ+1];</code></p>
    <p><code> char *new = newname, *old = oldname;</code></p>
    <empty-line/>
    <p><code> for (;;) {</code></p>
    <p><code>  while (*old == '/') /* skip slashes */</code></p>
    <p><code>   *new++ = *old++;</code></p>
    <p><code>  *new = '\0';</code></p>
    <p><code>  if (*old == '\0') /* exact or corrected */</code></p>
    <p><code>   return strcmp(oldname, newname) != 0;</code></p>
    <p><code>  p = guess; /* copy next component into guess */</code></p>
    <p><code>  for (; *old != '/' &amp;&amp; *old != '\0'; old++)</code></p>
    <p><code>   if (p &lt; guess+DIRSIZ)</code></p>
    <p><code>    *p++ = *old;</code></p>
    <p><code>   *p = '\0';</code></p>
    <p><code>   if (mindist(newname, guess, best) &gt;= 3)</code></p>
    <p><code>    return -1; /* hopeless */</code></p>
    <p><code>   for (p = best; *new = *p++; ) /* add to end */</code></p>
    <p><code>    new++; /* of newname */</code></p>
    <p><code> }</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>mindist(dir, guess, best) /* search dir for guess */</code></p>
    <p><code> char *dir, *guess, *best;</code></p>
    <p><code>{</code></p>
    <p><code> /* set best, return distance 0..3 */</code></p>
    <p><code> int d, nd, fd;</code></p>
    <p><code> struct {</code></p>
    <p><code>  ino_t ino;</code></p>
    <p><code>  char name[DIRSIZ+1]; /* 1 more than in dir.h */</code></p>
    <p><code> } nbuf;</code></p>
    <empty-line/>
    <p><code> nbuf.name[DIRSIZ] = '\0'; /* +1 for terminal '\0' */</code></p>
    <p><code> if (dir[0] == '\0') /* current directory */</code></p>
    <p><code>  dir = ".";</code></p>
    <p><code> d = 3; /* minimum distance */</code></p>
    <p><code> if ((fd = open(dir, 0)) == -1)</code></p>
    <p><code>  return d;</code></p>
    <p><code> while (read(fd,(char *)&amp;nbuf, sizeof(struct direct)) &gt; 0)</code></p>
    <p><code>  if (nbuf.ino) {</code></p>
    <p><code>   nd = spdist(nbuf.name, guess);</code></p>
    <p><code>   if (nd &lt;= d &amp;&amp; nd != 3) {</code></p>
    <p><code>    strcpy(best, nbuf.name);</code></p>
    <p><code>    d = nd;</code></p>
    <p><code>    if (d == 0) /* exact match */</code></p>
    <p><code>     break;</code></p>
    <p><code>   }</code></p>
    <p><code>  }</code></p>
    <p><code> close(fd);</code></p>
    <p><code> return d;</code></p>
    <empty-line/>
    <p>Если имя каталога, данное <code>mindist</code>, пустое, отыскивается <code>'.'</code>. Функция <code>mindist</code> читает одну строку каталога за один раз. Отметим, что буфер для <code>read</code> представляет собой структуру, а не массив символов. Мы используем <code>sizeof</code>, чтобы вычислить число байтов и привести адрес к символьному указателю.</p>
    <p>Если строка каталога в данный момент не используется (поскольку файл удален), то поле индекса в ней равно нулю и она пропускается. Проверка расстояния осуществляется как</p>
    <p><code>if (nd &lt;= d...)</code></p>
    <p>а не как</p>
    <p><code>if (nd &lt; d...)</code></p>
    <p>поэтому любой одиночный символ дает лучшее совпадение, чем имя <code>'.'</code>, которое всегда является первой строкой в каталоге.</p>
    <p><code>/* spdist: return distance between two names */ /*</code></p>
    <p><code> * very rough spelling metric:</code></p>
    <p><code> * 0 if the strings are identical</code></p>
    <p><code> * 1 if two chars are transposed</code></p>
    <p><code> * 2 if one char wrong, added or deleted</code></p>
    <p><code> * 3 otherwise</code></p>
    <p><code> */</code></p>
    <p><code>#define EQ(s,t) (strcmp(s,t) == 0)</code></p>
    <empty-line/>
    <p><code>spdist(s, t)</code></p>
    <p><code> char *s, *t;</code></p>
    <p><code>{</code></p>
    <p><code> while (*s++ == *t)</code></p>
    <p><code>  if (*t++ == '\0')</code></p>
    <p><code>   return 0; /* exact match */</code></p>
    <p><code> if (*--s) {</code></p>
    <p><code>  if (*t) {</code></p>
    <p><code>   if (s[1] &amp;&amp; t[1] &amp;&amp; *s == t[1] &amp;&amp; *t == s[1] &amp;&amp; EQ(s+2, t+2))</code></p>
    <p><code>    return 1; /* transposition */</code></p>
    <p><code>   if (EQ(s+1, t+1))</code></p>
    <p><code>    return 2; /* 1 char mismatch */</code></p>
    <p><code>  }</code></p>
    <p><code>  if (EQ(s+1, t))</code></p>
    <p><code>   return 2; /* extra character */</code></p>
    <p><code> }</code></p>
    <p><code> if (*t &amp;&amp; EQ(s, t+1))</code></p>
    <p><code>  return 2; /* missing character */</code></p>
    <p><code> return 3;</code></p>
    <p><code>}</code></p>
    <p>Поскольку у нас есть <code>spname</code>, несложно вставить функции по коррекции написания в <code>p</code>:</p>
    <p><code>/* p: print input in chunks (version 4) */</code></p>
    <empty-line/>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#define PAGESIZE 22</code></p>
    <empty-line/>
    <p><code>char *progname; /* program name for error message */</code></p>
    <empty-line/>
    <p><code>main(argc, argv)</code></p>
    <p><code> int argc;</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> FILE *fp, *efopen();</code></p>
    <p><code> int i, pagesize = PAGESIZE;</code></p>
    <p><code> char *p, *getenv(), buf[BUFSIZ];</code></p>
    <empty-line/>
    <p><code> progname = argv[0];</code></p>
    <p><code> if ((p=getenv("PAGESIZE")) != NULL)</code></p>
    <p><code>  pagesize = atoi(p);</code></p>
    <p><code> if (argc &gt; 1 &amp;&amp; argv[1][0] == '-') {</code></p>
    <p><code>  pagesize = atoi(&amp;argv[1][1]);</code></p>
    <p><code>  argc--;</code></p>
    <p><code>  argv++;</code></p>
    <p><code> }</code></p>
    <p><code> if (argc == 1)</code></p>
    <p><code>  print(stdin, pagesize);</code></p>
    <p><code> else</code></p>
    <p><code>  for (i = 1; i &lt; argc; i++)</code></p>
    <p><code>   switch (spname(argv[i], buf)) {</code></p>
    <p><code>   case -1: /* no match possible */</code></p>
    <p><code>    fp = efopen(argv[i], "r");</code></p>
    <p><code>    break;</code></p>
    <p><code>   case 1: /* corrected */</code></p>
    <p><code>    fprintf(stderr, "\"%s\"? ", buf);</code></p>
    <p><code>    if (ttyin() == 'n')</code></p>
    <p><code>     break;</code></p>
    <p><code>    argv[i] = buf; /* fall through... */</code></p>
    <p><code>   case 0: /* exact match */</code></p>
    <p><code>    fp = efopen(argv[i], "r");</code></p>
    <p><code>    print(fp, pagesize);</code></p>
    <p><code>    fclose(fp);</code></p>
    <p><code>   }</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <p>Функции по коррекции написания не следует слепо применять к каждой программе, которая использует имена файлов. Они хорошо сочетаются с <code>p</code>, так как <code>p</code> — диалоговая программа, но подходят и для недиалоговых программ.</p>
    <cite>
     <subtitle>Упражнение 7.5</subtitle>
     <p>Насколько вы можете улучшить эвристику для выявления наилучшего совпадения в <code>spname</code>? Например, неразумно рассматривать регулярный файл так, как если бы он был каталогом; текущая версия это допускает.</p>
    </cite>
    <cite>
     <subtitle>Упражнение: 7.6</subtitle>
     <p>Имя <code>tx</code> совпадает с каким-либо именем <code>tc</code>, которое оказывается последним в каталоге для любого одиночного символа <code>с</code>. Можете ли вы придумать лучшую меру расстояния? Реализуйте ее и посмотрите, насколько хорошо эта конструкция работает с реальными пользователями.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 7.7</subtitle>
     <p>Работает ли <code>p</code> ощутимо быстрее, если чтение каталога выполняется большими порциями?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 7.8</subtitle>
     <p>Модифицируйте <code>spname</code>, чтобы возвращать имя, которое является префиксом желаемого имени, если нельзя найти более точного совпадения. Как следует разрешить ситуацию, если несколько имен совпадают с префиксом?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 7.9</subtitle>
     <p>Какую пользу могли бы извлечь другие программы из <code>spname</code>? Сконструируйте отдельную программу, которая корректировала бы другие аргументы прежде, чем передать их другой программе, как в</p>
     <p><code>$ fix prog filenames...</code></p>
     <p>Можете написать версию <code>cd</code>, которая использует <code>spname</code>? Как бы вы ее встроили?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>7.3 Файловая система: индексные дескрипторы</p>
    </title>
    <p>Теперь мы обсудим системные вызовы применительно к файловой системе, в частности к такой информации о файлах, как размеры, даты изменений, права доступа и т.д. Эти системные вызовы позволяют получить полностью всю информацию, о которой упоминалось во второй главе.</p>
    <p>Для начала разберемся в самом индексном дескрипторе. Часть индексного дескриптора описывается структурой stat, определенной в <code>&lt;sys/stat.h&gt;</code>:</p>
    <p><code>struct stat /* структура, возвращаемая stat */</code></p>
    <p><code>{</code></p>
    <p><code> dev_t st_dev;    /* устройство, содержащее файл */</code></p>
    <p><code> ino_t st_ino;    /* индекс */</code></p>
    <p><code> short st_mod;    /* биты режима */</code></p>
    <p><code> short st_nlink;  /* число связей файла */</code></p>
    <p><code> short st_uid;    /* пользовательский идентификатор</code></p>
    <p><code>                     владельца */</code></p>
    <p><code> short st_gid;    /* идентификатор группы владельцев */</code></p>
    <p><code> dev_t st_rdev;   /* для специальных файлов */</code></p>
    <p><code> off_t st_size;   /* размер файла в символах */</code></p>
    <p><code> time_t st_atime; /* время последнего чтения файла */</code></p>
    <p><code> time_t st_mtime; /* время последней записи</code></p>
    <p><code>                     или создания файла */</code></p>
    <p><code> time_t st_ctime; /* время последнего изменения</code></p>
    <p><code>                     индексного дескриптора или файла */</code></p>
    <p><code>}</code></p>
    <p>Большинство полей поясняются примечаниями. Типы вроде <code>dev_t</code> и <code>ino_t</code> определены в <code>&lt;sys/types.h&gt;</code>, как отмечено выше. Поле <code>st_mode</code> содержит множество флагов, описывающих файл; для удобства определения флагов также являются частью файла <code>&lt;sys/stat.h&gt;</code>:</p>
    <p><code>#define S_IFMT   0170000  /* тип файла */</code></p>
    <p><code>#define  S_IFDIR 0040000  /* каталог */</code></p>
    <p><code>#define  S_IFCHR 0020000  /* байт-ориентированный */</code></p>
    <p><code>#define  S_IFBLK 0060000  /* блок-ориентированный */</code></p>
    <p><code>#define  S_IFREG 0100000  /* регулярный */</code></p>
    <p><code>#define S_SUID   0004000  /* установка идентификатора пользователя при</code></p>
    <p><code>                             выполнении */</code></p>
    <p><code>#define S_ISGID  0002000  /* установка идентификатора группы</code></p>
    <p><code>                             при выполнении */</code></p>
    <p><code>#define S_ISVTX  0001000  /* сохранить выгруженный текст даже после</code></p>
    <p><code>                             использования */</code></p>
    <p><code>#define S_IREAD  0000400  /* разрешение читать, владелец */</code></p>
    <p><code>#define S_IWRITE 0000200  /* разрешение писать, владелец */</code></p>
    <p><code>#define S_IEXEC  0000100  /* разрешение на выполнение/поиск, владелец */</code></p>
    <p>Индексный дескриптор для файла доступен двум системным вызовам <code>stat</code> и <code>fstat</code>. При вызове <code>stat</code> параметром является имя файла, а результатом — информация из индексного дескриптора для этого файла (или — 1 при наличии ошибки). <code>Fstat</code> выполняет те же функции в отношении дескриптора открытого файла (не в отношении указателя на <code>FILE</code>). Иными словами,</p>
    <p><code>char *name;</code></p>
    <p><code>int fd;</code></p>
    <p><code>struct stat stbuf;</code></p>
    <empty-line/>
    <p><code>stat(name, &amp;stbuf);</code></p>
    <p><code>fstat(fd, &amp;stbuf);</code></p>
    <p>заполняет структуру <code>stbuf</code> информацией из индексного дескриптора для имени файла или дескриптора файла <code>fd</code>.</p>
    <p>Зная все это, мы можем приступить к написанию некоторой полезной программы. Начнем с версии <code>checkmail</code> — программы на Си, которая следит за содержимым вашего почтового ящика. Если файл увеличивается, <code>checkmail</code> выдает сообщение: "У вас есть корреспонденция" и включает звонок. (При уменьшении файла, видимо, из-за того, что вы успели прочитать и сбросить некоторую почтовую корреспонденцию, сообщение не требуется.) Для первого шага вы сделали вполне достаточно, а когда ваша программа заработает, вы станете знатоком.</p>
    <p><code>/* checkmail: watch user's mailbox */</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#include &lt;sys/types.h&gt;</code></p>
    <p><code>#include &lt;sys/stat.h&gt;</code></p>
    <empty-line/>
    <p><code>char *progname;</code></p>
    <p><code>char *maildir = "/usr/spool/mail"; /* system dependent */</code></p>
    <empty-line/>
    <p><code>main(argc, argv)</code></p>
    <p><code> int argc;</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> struct stat buf;</code></p>
    <p><code> char *name, *getlogin();</code></p>
    <p><code> int lastsize = 0;</code></p>
    <empty-line/>
    <p><code> progname = argv[0];</code></p>
    <p><code> if ((name = getlogin()) == NULL)</code></p>
    <p><code>  error("can't get login name", (char*)0);</code></p>
    <p><code> if (chdir(maildir) == -1)</code></p>
    <p><code>  error("can't cd to %s", maildir);</code></p>
    <p><code> for (;;) {</code></p>
    <p><code>  if (stat(name, &amp;buf) == -1) /* no mailbox */</code></p>
    <p><code>  buf.st_size = 0;</code></p>
    <p><code>  if (buf.st_size &gt; lastsize)</code></p>
    <p><code>  fprintf(stderr, "\nYou have mail\007\n");</code></p>
    <p><code>  lastsize = buf.st_size;</code></p>
    <p><code>  sleep(60);</code></p>
    <p><code> }</code></p>
    <p><code>}</code></p>
    <p>Функция <code>getlogin(3)</code> возвращает ваше регистрационное имя или <code>NULL</code>, если это невозможно, <code>checkmail</code> переходит к почтовому каталогу с помощью системного вызова <code>chdir</code>, так что последующие вызовы <code>stat</code> не должны будут "добираться" до почтового каталога через все каталоги, начиная от корневого. Возможно, вы должны адаптировать <code>maildir</code> для своей системы. Мы написали <code>checkmail</code> так, чтобы она работала, даже если нет почтового ящика, поскольку большинство версий mail убирают почтовый ящик в том случае, когда он пуст.</p>
    <p>Мы приводили эту программу в гл. 5 для иллюстрации циклов <code>shell</code>. Всякий раз при проверке почтового ящика она создает несколько процессов и загружает систему больше, чем хотелось бы. Версия на Си — единственный процесс, который выполняет <code>stat</code> для файла каждую минуту. Сколько времени требуется на то, чтобы <code>checkmail</code> постоянно выполнялась как фоновая задача? Как показали наши измерения, это время составляет меньше секунды в час, так что им вполне можно пренебречь.</p>
    <subtitle><code>sv</code>: иллюстрация обработки ошибок</subtitle>
    <p>Следующей мы собираемся написать похожую на <code>cp</code> программу <code>sv</code>, которая будет копировать множество файлов в каталог, заменяя каждый файл лишь в том случае, если его нет в каталоге или он "старше" копируемого с тем же именем (имя <code>sv</code> означает "сохранять"). Суть действия программы состоит в том, что она не переписывает новую версию файла, <code>sv</code> использует больше информации из индексного дескриптора, чем <code>checkmail</code>. Вызов <code>sv</code> будет иметь такую конструкцию:</p>
    <p><code>$ sv file1 file2 ... dir</code></p>
    <p>Она копирует <code>file1</code> в <code>dir/file1</code>, <code>file2</code> в <code>dir/file2</code> и т.д., если только целевой файл не новее, чем файл-источник; в этой ситуации копирование не происходит и печатается соответствующее предупреждение. Во избежание создания большого числа копий или связанных файлов <code>sv</code> не допускает применения символов <code>'/'</code> в любом исходном имени файла.</p>
    <p><code>/* sv: save new files */</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#include &lt;sys/types.h&gt;</code></p>
    <p><code>#include &lt;sys/dir.h&gt;</code></p>
    <p><code>#include &lt;sys/stat.h&gt;</code></p>
    <empty-line/>
    <p><code>char *progname;</code></p>
    <empty-line/>
    <p><code>main(argc, argv)</code></p>
    <p><code> int argc;</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> int i;</code></p>
    <p><code> struct stat stbuf;</code></p>
    <empty-line/>
    <p><code> char *dir = argv[argc-1];</code></p>
    <p><code> progname = argv[0];</code></p>
    <p><code> if (argc &lt;= 2)</code></p>
    <p><code>  error("Usage: %s files... dir", progname);</code></p>
    <p><code> if (stat(dir, &amp;stbuf) == -1)</code></p>
    <p><code>  error("can't access directory %s", dir);</code></p>
    <p><code> if ((stbuf.st_mode &amp; S_IFMT) != S_IFDIR)</code></p>
    <p><code>  error("%s is not a directory", dir);</code></p>
    <p><code> for (i = 1; i &lt; argc-1; i++)</code></p>
    <p><code>  sv(argv[i], dir);</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <p>Значения времени, хранящиеся в индексных дескрипторах, исчисляются в секундах (за начало отсчета принято время 0:00 по Гринвичу, 1 января 1970 г.), так что более старые файлы имеют меньшие значения в поле <code>st_mtime</code>.</p>
    <p><code>sv(file, dir) /* save file in dir */</code></p>
    <p><code> char *file, *dir;</code></p>
    <p><code>{</code></p>
    <p><code> struct stat sti, sto;</code></p>
    <p><code> int fin, fout, n;</code></p>
    <p><code> char target[BUFSIZ], buf[BUFSIZ], *index();</code></p>
    <empty-line/>
    <p><code> sprintf(target, "%s/%s", dir, file);</code></p>
    <p><code> if (index(file, '/') != NULL) /* strchr() in some systems */</code></p>
    <p><code>  error("won't handle /'s in %s", file);</code></p>
    <p><code> if (stat(file, &amp;sti) == -1)</code></p>
    <p><code>  error("can't stat %s", file);</code></p>
    <p><code> if (stat(target, &amp;sto) == -1) /* target not present */</code></p>
    <p><code>  sto.st_mtime = 0; /* so make it look old */</code></p>
    <p><code> if (sti.st_mtime &lt; sto.st_mtime) /* target is newer */</code></p>
    <p><code>  fprintf(stderr, "%s: %s not copied\n", progname, file);</code></p>
    <p><code> else if ((fin = open(file, 0)) == -1)</code></p>
    <p><code>  error("can't open file %s", file);</code></p>
    <p><code> else if ((fout = creat(target, sti.st_mode)) == -1)</code></p>
    <p><code>  error("can't create %s", target);</code></p>
    <p><code> else</code></p>
    <p><code>  while ((n = read(fin, buf, sizeof buf)) &gt; 0)</code></p>
    <p><code>   if (write(fout, buf, n) != n)</code></p>
    <p><code>    error("error writing %s", target);</code></p>
    <p><code> close(fin);</code></p>
    <p><code> close(fout);</code></p>
    <p><code>}</code></p>
    <p>Мы заменили стандартные функции ввода-вывода функцией <code>creat</code>, так что <code>sv</code> может сохранять режим работы входного файла. (Заметьте, что <code>index</code> и <code>strchr</code> — разные имена одной и той же процедуры; посмотрите в справочном руководстве по <code>string(3)</code>, какое имя использует ваша система.)</p>
    <p>Хотя программа <code>sv</code> довольно специфична, в ней отражены некоторые важные идеи. Многие программы не являются системными, но тем не менее используют информацию, поддерживаемую операционной системой и доступную через системные вызовы. Для таких программ существенно, что представление информации хранится только в стандартных файлах макроопределений типа <code>&lt;stat.h&gt;</code> и <code>&lt;dir.h&gt;</code> и что в программы включены эти файлы вместо действительных описаний. Подобные программы с большей степенью вероятности переносимы с одной системы на другую.</p>
    <p>Отметим, что по крайней мере две трети кода <code>sv</code> составляет контроль ошибок. На ранних этапах написания программы было искушение сэкономить на обработке ошибок, поскольку это отвлекает от основной задачи. Когда же программа уже работает, трудно решиться на то, чтобы вернуться назад и вставить в нее процедуры проверки, которые превращают специальную программу в унифицированную.</p>
    <p>Программа <code>sv</code> не защищена от возможных сбоев. Она, например, не обрабатывает прерывания в неподходящие моменты, но более аккуратна, чем большинство других программ. Хотелось бы обратить ваше внимание на финальный оператор <code>write</code>. Программа редко сбивается на этом операторе, поэтому многие программы игнорируют такую возможность. Однако переполнение дискового пространства, неполадки в линии связи или иные нарушения могут вызвать ошибки в <code>write</code>, и вы гораздо лучше справитесь с ними, если программа выдает вам соответствующее сообщение.</p>
    <p>Дело в том, что контроль ошибок весьма утомителен, но тем не менее важен. Из-за ограниченного объема книги и обширности излагаемого в ней материала мы не уделяли должного внимания этому вопросу. Но в настоящих, "производственных" программах не следует позволять себе игнорировать ошибки.</p>
    <cite>
     <subtitle>Упражнение 7.10</subtitle>
     <p>Модифицируйте <code>checkmail</code> так, чтобы идентифицировать посылающего сообщение: "У вас есть почта". <emphasis>Подсказка</emphasis>: <code>sscanf</code>, <code>lseek</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 7.11</subtitle>
     <p>Модифицируйте <code>checkmail</code> так, чтобы она не переходила к каталогу почты перед входом в цикл. Окажет ли это ощутимое влияние на ее производительность? Более трудный вопрос: можете ли вы написать версию <code>checkmail</code>, которая обходится только одним процессом для оповещения всех пользователей?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 7.12</subtitle>
     <p>Напишите программу <code>watchfile</code>, которая управляет файлом и печатает его с начала всякий раз, как он изменится. Когда бы вы ее использовали?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 7.13</subtitle>
     <p>Программа <code>sv</code> очень "прямолинейна" при обработке ошибок. Модифицируйте ее так, чтобы она продолжала выполняться, даже если не удается обработать некоторый файл.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 7.14</subtitle>
     <p>Сделайте <code>sv</code> рекурсивной: если один из исходных файлов — каталог, то этот каталог и все его файлы обрабатываются таким же образом. Сделайте рекурсивной <code>cp</code>. Подумайте, следует ли <code>cp</code> и <code>sv</code> объединить в одну программу, чтобы <code>cp -v</code> не создавала копию, если целевой файл новее файла-источника.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 7.15</subtitle>
     <p>Напишите программу <code>random</code>.</p>
     <p><code>$ random filename</code></p>
     <p>должна выдавать одну строку, произвольно выбранную из файла. Если есть файл <code>people</code>, содержащий имена, <code>random</code> можно использовать в программе <code>scapegoat</code> ("козел отпущения"), полезной при случайном определении виновных:</p>
     <p><code>$ cat scapegoat</code></p>
     <p><code>echo "В этом виноват `random people`!"</code></p>
     <p><code>$ scapegoat</code></p>
     <p><code>В этом виноват Кен!</code></p>
     <p><code>$</code></p>
     <p>Убедитесь в том, что <code>random</code> хороша независимо от распределения длины строк.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 7.16</subtitle>
     <p>Помимо прочего в индексном дескрипторе указаны адреса размещения блоков файла на диске. Рассмотрите файл <code>&lt;sys/into.h&gt;</code>, а затем напишите программу <code>icat</code>, которая должна читать файлы, описываемые номером записи каталога и устройством диска. (Она, конечно, будет работать только в том случае, если требуемый диск открыт на чтение.) При каких обстоятельствах <code>icat</code> полезна?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>7.4 Процессы</p>
    </title>
    <p>В этом разделе мы покажем вам, как выполнить одну программу, вызвав ее из другой. Самый легкий путь — привлечь стандартную библиотечную программу <code>system</code>, упомянутую, но забракованную в гл. 6. Программа <code>system</code> использует один аргумент — командную строку в том виде, в каком она вводится с терминала (за исключением символа перевода строки), и выполняет ее порожденным <code>shell</code>. Если командная строка должна быть создана из кусочков, можно прибегнуть к форматированию памяти программой <code>sprintf</code>. В конце раздела мы рассмотрим более безопасную версию <code>system</code> для работы с диалоговыми программами, но прежде чем изучать программу в целом, обсудим структуры, из которых она составляется.</p>
    <subtitle>Создание процесса низкого уровня: <code>execlp</code> и <code>execvp</code></subtitle>
    <p>Самая важная операция - выполнение другой программы без возврата с помощью системного вызова <code>execlp</code>. Например, чтобы напечатать дату и выполнить тем самым последнее действие запущенной программы, используют</p>
    <p><code>execlp("date", "date", (char*)0);</code></p>
    <p>Первый аргумент <code>execlp</code> есть имя файла команды; <code>execlp</code> выбирает путь поиска (т.е. <code>$PATH</code>) из вашего окружения и выполняет такой же поиск, как <code>shell</code>. Второй и последующие аргументы — это имена и аргументы команд; для новой программы они становятся массивом <code>argv</code>. Конец списка отмечен аргументом 0. (См. справочное руководство по <code>exec(2)</code>, и вы поймете конструкцию <code>execlp</code>.)</p>
    <p>Вызов <code>execlp</code> перекрывает существующую программу новой, запускает ее и затем завершается. Первоначальная программа получает управление обратно только при возникновении ошибки, например, когда файл не удается найти или он является невыполнимым:</p>
    <p><code>execlp("date", "date", (char*)0);</code></p>
    <p><code>fprintf(stderr, "Не удалось выполнить 'date'\n");</code></p>
    <p><code>exit(1);</code></p>
    <p>Если число аргументов вам заранее не известно, полезно применить <code>execvp</code> (вариант <code>execlp</code>). Вызов выглядит так:</p>
    <p><code>execvp(filename, argp);</code></p>
    <p>где <code>argp</code> означает массив указателей к аргументам (таким, как <code>argv</code>). Последним в массиве должен быть указатель <code>NULL</code>, так что <code>execvp</code> может отметить конец списка. Как и для <code>execlp</code>, <code>filename</code> — это файл, в котором находится программа, <code>argp</code> — массив <code>argv</code> для новой программы, a <code>argp[0]</code> — имя программы.</p>
    <p>Ни одна из перечисленных выше программ не обеспечивает расширения в списке аргументов метасимволов типа <code>&lt;</code>, <code>&gt;</code>, <code>*</code>, кавычки и т.п. Если они вам нужны, воспользуйтесь <code>execlp</code> и вызовите <code>/bin/sh</code> из <code>shell</code>, которая выполнит эту работу. Сконструируйте строку <code>commandline</code>, содержащую полную команду, как если бы она была напечатана на терминале, например:</p>
    <p><code>execlp("/bin/sh/", "sh", "-с", commandline, (char*)0);</code></p>
    <p>Аргумент <code>-с</code> предписывает трактовать следующий аргумент как целую командную строку.</p>
    <p>В качестве иллюстрации <code>exec</code> рассмотрим программу <code>waitfile</code>. Команда</p>
    <p><code>$ waitfile filename [command]</code></p>
    <p>периодически проверяет поименованный файл. Если он не менялся после последней проверки, выполняется <code>command</code>. В том случае, когда команда не указана, файл копируется в стандартный выходной поток. С помощью <code>waitfile</code> мы контролируем работу <code>troff</code>, как в</p>
    <p><code>$ waitfile troff .out echo troff done &amp;</code></p>
    <p>Программа <code>waitfile</code> использует <code>fstat</code>, чтобы выявить время последнего изменения файла.</p>
    <p><code>/* waitfile: wait until file stops changing */</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#include &lt;sys/types.h&gt;</code></p>
    <p><code>#include &lt;sys/stat.h&gt;</code></p>
    <p><code>char *progname;</code></p>
    <empty-line/>
    <p><code>main(argc, argv)</code></p>
    <p><code> int argc;</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> int fd;</code></p>
    <p><code> struct stat stbuf;</code></p>
    <p><code> time_t old_time = 0;</code></p>
    <empty-line/>
    <p><code> progname = argv[0];</code></p>
    <p><code> if (argc &lt; 2)</code></p>
    <p><code>  error("Usage: %s filename [cmd]", progname);</code></p>
    <p><code> if ((fd = open(argv[1], 0)) == -1)</code></p>
    <p><code>  error("can't open %s", argv[1]);</code></p>
    <p><code> fstat(fd, &amp;stbuf);</code></p>
    <p><code> while(stbuf.st_mtime != old_time) {</code></p>
    <p><code>  old_time = stbuf.st_mtime;</code></p>
    <p><code>  sleep(60);</code></p>
    <p><code>  fstat(fd, &amp;stbuf);</code></p>
    <p><code> }</code></p>
    <p><code> if (argc == 2) { /* copy file */</code></p>
    <p><code>  execlp("cat", "cat", argv[1], (char*)0);</code></p>
    <p><code>  error("can't execute cat %s", argv[1]);</code></p>
    <p><code> } else { /* run process */</code></p>
    <p><code>  execvp(argv[2], &amp;argv[2]);</code></p>
    <p><code>  error("can't execute %s", argv[2]);</code></p>
    <p><code> }</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <p>Мы рассмотрели пример работы как <code>execlp</code>, так и <code>execvp</code>. Эта программа выбрана в качестве иллюстрации, поскольку она весьма полезна, но возможны и другие варианты. Так, <code>waitfile</code> могла бы просто завершиться по окончании изменения файла.</p>
    <cite>
     <subtitle>Упражнение 7.17</subtitle>
     <p>Модифицируйте <code>watchfile</code> (упр. 7.12) так, чтобы она имела то же свойство, что и <code>waitfile</code>: в отсутствие <code>command</code> копируется файл, в противном случае выполняется команда. Могли бы <code>watchfile</code> и <code>waitfile</code> разделять исходную программу? Подсказка: <code>argv[0]</code>.</p>
    </cite>
    <subtitle>Управление процессами: <code>fork</code> и <code>wait</code></subtitle>
    <p>Следующий шаг — вновь получить управление после запуска программы с помощью <code>execlp</code> и <code>execvp</code>. Так как эти программы просто "перекрывают" старую программу новой, для сохранения старой требуется сначала разбить ее на две копии. Одна из копий может быть перекрыта, в то время как другая ждет новую, перекрывающую ее программу, чтобы завершиться. Разбиение выполняется с помощью системного вызова <code>fork</code>:</p>
    <p><code>proc_id = fork();</code></p>
    <p>Программа разбивается на две копии, каждая из которых продолжает работать. Они отличаются лишь значением, возвращаемым <code>fork</code>, — номером процесса <code>process-id</code>. В первом процессе (<emphasis>потомке</emphasis>) <code>proc_id</code> равен нулю, во втором (<emphasis>родительском</emphasis>) <code>proc_id</code> есть номер процесса-потомка. Итак, вызвать другую программу и вернуться можно следующим образом:</p>
    <p><code>if (fork() == 0)</code></p>
    <p><code> execlp("/bin/sh", "sh", "-с", commandline, (char*)0);</code></p>
    <p>Фактически этого достаточно, за исключением обработки ошибок. <code>Fork</code> делает две копии программы. В процессе-потомке <code>fork</code> возвращает нуль, так что он вызывает <code>execlp</code>, которая выполняет <code>commandline</code> и затем завершается. В родительском процессе <code>fork</code> возвращает не нуль, поэтому <code>execlp</code> пропускается. (При наличии ошибки <code>fork</code> возвращает -1-)</p>
    <p>Чаще родительский процесс ожидает, пока потомок закончит работу, прежде чем продолжить свое выполнение, для чего используется системный вызов <code>wait</code>:</p>
    <p><code>int status;</code></p>
    <empty-line/>
    <p><code>if (fork() == 0)</code></p>
    <p><code> execlp(...); /* потомок */</code></p>
    <p><code>wait(&amp;status); /* родитель */</code></p>
    <p>Однако при этом не контролируются ошибки, такие, как сбои <code>execlp</code> и <code>fork</code>, или возможность одновременной работы нескольких процессов-потомков (<code>wait</code> возвращает номер завершившегося процесса-потомка, если вы хотите сравнить его со значением, возвращенным <code>fork</code>). Тем не менее эти три строки являются сердцевиной стандартной функции <code>system</code>.</p>
    <p>Значение <code>status</code>, возвращаемое <code>wait</code>, содержит в своих младших восьми разрядах системное представление кода завершения процесса-потомка; оно равно нулю при нормальном завершении и не равно нулю при разного рода затруднениях. Следующие старшие восемь битов берутся из аргумента вызова <code>exit</code> или возвращаются из <code>main</code>, которая вызывает окончание выполнения процесса-потомка.</p>
    <p>Если программа вызывается из <code>shell</code>, три дескриптора файла, 0, 1 и 2, ссылаются на соответствующие файлы, и все остальные дескрипторы доступны для использования. Когда эта программа вызывает другую, в соответствии с профессиональной этикой указанные условия должны быть соблюдены. Ни <code>fork</code>, ни <code>exec</code> не влияют никоим образом на открытые файлы; оба процесса, родитель и потомок, имеют одни и те же открытые файлы. Если процесс-родитель буферизует выходной поток, который необходимо вывести до процесса-потомка, родитель должен очистить свой буфер ранее <code>execlp</code>. И, наоборот, при буферизации родителем входного потока потомок потеряет информацию, которая читалась родителем. Выходной поток может быть выведен, но входной нельзя "положить назад". Обе ситуации являются следствием реализации входного или выходного потока стандартной библиотекой ввода-вывода, обсуждавшейся в гл. 6, поскольку при этом и ввод, и вывод буферизуются обычным образом.</p>
    <p>Именно свойство наследования дескрипторов файлов через <code>execlp</code> используется в <code>system</code>: если у вызывающей программы стандартные входной и выходной потоки не связаны с терминалом, то этим же свойством обладает команда, вызванная из <code>system</code>. Возможно, такой вариант нам и нужен. В списке команд редактора <code>ed</code>, например, входной поток команды, начинающейся с символа <code>!</code>, вероятно, должен поступить из того же списка. Даже тогда <code>ed</code> должен считывать из своего входного потока по одному символу во избежание возникновения проблем буферизации ввода.</p>
    <p>Для диалоговых программ, подобных p, <code>system</code> должна тем не менее вновь связать стандартный входной и выходной потоки с терминалом, в частности <code>/dev/tty</code>.</p>
    <p>Системный вызов <code>dup(fd)</code> дублирует дескриптор файла <code>fd</code> на незанятый дескриптор файла с наименьшим номером и возвращает новый дескриптор, ссылающийся на тот же самый открытый файл. Следующая программа "присоединяет" стандартный входной поток программы к файлу:</p>
    <p><code>int fd;</code></p>
    <empty-line/>
    <p><code>fd = open("file", 0);</code></p>
    <p><code>close(0);</code></p>
    <p><code>dup(fd);</code></p>
    <p><code>close(fd);</code></p>
    <p>Вызов <code>close(fd)</code> освобождает дескриптор файла 0 (стандартный входной поток), но, как правило, не влияет на процесс-родитель. Здесь приведена наша версия <code>system</code> для диалоговых программ, использующая <code>progname</code> для вывода сообщений об ошибках. Вам следует игнорировать те части функции, которые имеют дело с сигналами (мы вернемся к ним позднее).</p>
    <p><code>/*</code></p>
    <p><code> * Safer version of system for interactive programs</code></p>
    <p><code> */</code></p>
    <p><code>#include &lt;signal.h&gt;</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <empty-line/>
    <p><code>system(s) /* run command line s */</code></p>
    <p><code> char *s;</code></p>
    <p><code>{</code></p>
    <p><code> int status, pid, w, tty;</code></p>
    <p><code> int (*istat)(), (*qstat)();</code></p>
    <p><code> extern char *progname;</code></p>
    <p><code> fflush(stdout);</code></p>
    <p><code> tty = open("/dev/tty", 2);</code></p>
    <empty-line/>
    <p><code> if (tty == -1) {</code></p>
    <p><code>  fprintf(stderr, "%s: can't open /dev/tty\n", progname);</code></p>
    <p><code>  return -1;</code></p>
    <p><code> }</code></p>
    <p><code> if ((pid = fork()) == 0) {</code></p>
    <p><code>  close(0);</code></p>
    <p><code>  dup(tty);</code></p>
    <p><code>  close(1);</code></p>
    <p><code>  dup(tty);</code></p>
    <p><code>  close(2);</code></p>
    <p><code>  dup(tty);</code></p>
    <p><code>  close(tty);</code></p>
    <p><code>  execlp("sh", "sh", "-c", s, (char*)0);</code></p>
    <p><code>  exit(127);</code></p>
    <p><code> }</code></p>
    <p><code> close(tty);</code></p>
    <p><code> istat = signal(SIGINT, SIG_IGN);</code></p>
    <p><code> qstat = signal(SIGQUIT, SIG_IGN);</code></p>
    <p><code> while ((w = wait(&amp;status)) != pid &amp;&amp; w != -1)</code></p>
    <p><code>  ;</code></p>
    <p><code> if (w == -1)</code></p>
    <p><code>  status = -1;</code></p>
    <p><code> signal(SIGINT, istat);</code></p>
    <p><code> signal(SIGQUIT, qstat);</code></p>
    <p><code> return status;</code></p>
    <p><code>}</code></p>
    <p>Отметим, что <code>/dev/tty</code> открыта с режимом 2 — чтение и запись. С помощью <code>dup</code> формируются стандартный входной и выходной потоки. Здесь можно провести аналогию со сборкой системой стандартных входного и выходного потоков и потока ошибок, когда вы в нее входите. Поэтому в ваш стандартный входной поток можно писать:</p>
    <p><code>$ echo hello 1&gt;&amp;0</code></p>
    <p><code>hello</code></p>
    <p><code>$</code></p>
    <p>Это означает, что вам следует применить <code>dup</code> к дескриптору файла 2, чтобы вновь связать стандартные ввод и вывод, но открытие <code>/dev/tty</code> является более естественным и безопасным. Даже <code>system</code> имеет потенциальные проблемы: открытые файлы в вызывающей программе, такие, как <code>tty</code> в подпрограмме <code>ttin</code> программы <code>p</code>, будут передаваться процессу-потомку.</p>
    <p>Смысл изложенного выше состоит не в том, что вы должны использовать нашу версию <code>system</code> для своих программ (она могла бы разрушить недиалоговый <code>ed</code>, например), а в том, чтобы понять, как управляют процессами и корректно используют примитивы; значение слова "корректно" меняется в зависимости от приложения и может быть не согласовано со стандартной реализацией <code>system</code>.</p>
   </section>
   <section>
    <title>
     <p>7.5 Сигналы и прерывания</p>
    </title>
    <p>Теперь мы рассмотрим работу с сигналами извне (такими, как прерывания) и ошибками программы. Последние возникают главным образом из-за некорректных обращений к памяти, выполнения привилегированных команд или при выполнении операций с плавающей запятой. Наиболее распространенными внешними сигналами являются <emphasis>прерывание</emphasis>, посылаемый при печати символа <emphasis>del</emphasis>, <emphasis>выйти</emphasis>, генерируемый символом FS (<emphasis>ctrl-\</emphasis>), <emphasis>отбой</emphasis>, вызываемый завершением телефонной связи, и закончить, генерируемый командой <code>kill</code>. Когда происходит одно из этих событий, посылается сигнал всем процессам, запущенным с того же терминала, и если не были приняты другие меры, процесс завершается. Для большинства сигналов пишется файл образа памяти, который может потребоваться при поиске ошибок (см. справочное руководство по <code>adb(1)</code>, <code>sdb(1)</code>).</p>
    <p>Системный вызов <code>signal</code> изменяет действие, заданное по умолчанию. Он имеет два аргумента: номер, определяющий сигнал, и адрес функции или код, предписывающий игнорировать сигнал либо запустить процедуру, принятую по умолчанию. Файл <code>&lt;signal.h&gt;</code> содержит определения для различных аргументов. Так,</p>
    <p><code>#include &lt;signal.h&gt;</code></p>
    <p><code>signal(SIGINT, SIG_IGN);</code></p>
    <p>Специфицирует игнорирование прерываний, тогда как</p>
    <p><code>signal(SIGINT, SIG_DEL);</code></p>
    <p>восстанавливает действие по умолчанию, означающее завершение процесса. В любом случае <code>signal</code> возвращает предыдущее значение сигнала. Если второй аргумент <code>signal</code> представляет собой имя функции, которая уже должна быть описана в том же самом исходном файле, то функция будет вызвана, когда возникнет сигнал. Это практикуется довольно часто, чтобы программа могла "подчищать" неоконченные работы перед своим завершением, например удалять временный файл:</p>
    <p><code>#include &lt;signal.h&gt;</code></p>
    <p><code>char *tempfile = "temp.xxxxxx";</code></p>
    <empty-line/>
    <p><code>main() {</code></p>
    <p><code> extern onintr();</code></p>
    <empty-line/>
    <p><code> if (signal(SIGINT, SIG_IGN) != SIG_IGN)</code></p>
    <p><code>  signal(SIGINT, onintr);</code></p>
    <p><code> mktemp(tempfile);</code></p>
    <p><code> /* Process ... */</code></p>
    <p><code> exit(0);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>onintr() { /* почистить, если прервано */</code></p>
    <p><code> unlink(tempfile);</code></p>
    <p><code> exit(1);</code></p>
    <p><code>}</code></p>
    <p>Почему в <code>main</code> имеют место проверки и двойной вызов <code>signal</code>? Вспомните, что сигналы посылаются всем процессам, запущенным с данного терминала. Соответственно если программа должна быть запущена не в диалоговом режиме (с помощью <code>&amp;</code>), <code>shell</code> делает так, что она будет игнорировать прерывания. Поэтому сигналы прерывания, посланные основным процессам, не остановят ее. Если бы эта программа началась с объявления о том, что все прерывания, которые должны быть посланы подпрограмме <code>onintr</code>, не принимаются во внимание, были бы сведены на нет все усилия <code>shell</code> защитить ее при запуске в фоновом режиме.</p>
    <p>Решение, показанное выше, состоит в том, чтобы проверить состояние обработки прерываний, если они игнорировались ранее. Функции программы в том виде, в каком она написана, зависят от возвращаемого <code>signal</code> предыдущего состояния конкретного сигнала. Если сигналы уже игнорировались, процесс должен продолжить это дело; в противном случае их следует перехватывать.</p>
    <p>Более сложная программа может перехватить прерывание и интерпретировать его как запрос на прекращение своих действий и возврат к основному циклу обработки команд. Подумаем о текстовом редакторе: прерывание длинного вывода на печать не должно вызывать завершения редактирования и потерю уже отредактированного текста. Программа для такого случая может быть написана следующим образом:</p>
    <p><code>#include &lt;signal.h&gt;</code></p>
    <p><code>#include &lt;setjmp.h&gt;</code></p>
    <empty-line/>
    <p><code>jmp_buf sjbuf;</code></p>
    <empty-line/>
    <p><code>main() {</code></p>
    <p><code> int onintr();</code></p>
    <empty-line/>
    <p><code> if(signal(SIGINT, SIG_IGN) != SIG_IGN)</code></p>
    <p><code>  signal(SIGINT, onintr);</code></p>
    <p><code> setjmp(sjbuf);</code></p>
    <p><code> /* сохранить текущую позицию стека */</code></p>
    <empty-line/>
    <p><code> for(;;) {</code></p>
    <p><code>  /* главный рабочий цикл */</code></p>
    <p><code> }</code></p>
    <p><code> ...</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>onintr() { /* установить если прервано */</code></p>
    <p><code> signal(SIGINT, onintr); /* установить</code></p>
    <p><code>                            для следующего прерывания */</code></p>
    <p><code> printf("\nInterrupt\n");</code></p>
    <p><code> longjmp(sjbuf, 0); /* вернуться</code></p>
    <p><code>                       в сохраненное состояние */</code></p>
    <p><code>}</code></p>
    <p>Файл <code>&lt;setjmp.h&gt;</code> описывает тип <code>jmp_buf</code> как объект, в котором сохраняется позиция стека; <code>sjbuf</code> считается таким объектом. Функция <code>setjmp(3)</code> сохраняет запись о том, где выполняется программа. Значения переменных <emphasis>не сохраняются</emphasis>. Когда происходит прерывание, выполняется обращение к подпрограмме <code>onintr</code>, которая может печатать сообщения, устанавливать флаги и т.д. Функция <code>longjmp</code> берет в качестве аргумента объект, сохраненный <code>setjmp</code>, и возвращает управление в ячейку после вызова <code>setjmp</code>. Поэтому управление (и значение уровня стека) будет возвращено обратно в основную программу — ко входу в головной цикл.</p>
    <p>Отметим, что после прерывания сигнал вновь настраивается на <code>onintr</code>. Это обусловлено тем, что когда сигналы возникают, они автоматически настраиваются на реакцию по умолчанию.</p>
    <p>Некоторые программы, которые "хотят" обнаружить сигналы, просто не могут быть остановлены в произвольный момент, например в середине обновления сложных составных данных. Решение состоит в том, что подпрограмма обработки прерывания должна установить флаг и вернуться к месту вызова <code>exit</code> или <code>longjmp</code>. Выполнение программы продолжится точно с того места, где оно было прервано, а флаг прерывания будет проверен позднее.</p>
    <p>С этим подходом связана одна трудность. Предположим, что, когда посылается сигнал прерывания, программа читается с терминала. Описанная подпрограмма непременно вызывается; она устанавливает свой флаг и возвращается. Если бы, как отмечалось выше, было верно то, что выполнение возобновляется точно с того места, где оно прервалось, программа продолжала бы чтение с терминала до ввода пользователем другой строки. Однако здесь возникает недоразумение, поскольку пользователь может не знать, что программа читает, и предположительно предпочел бы, чтобы сигнал сразу оказал действие. Для разрешения проблемы система должна закончить <code>read</code>, но с сообщением об ошибке, указывающим, что произошло: <code>errno</code> присваивается <code>EINTR</code>, определенное в заголовке <code>&lt;errno.h&gt;</code>, чтобы обозначить прерванный системный вызов.</p>
    <p>Так, программы, которые "ловят" сигналы и продолжают после этого свою работу, должны быть готовы к появлению ошибок, вызванных прерванными системными вызовами. (Следует остерегаться системных вызовов <code>read</code> — чтение с терминала, <code>wait</code>, <code>pause</code>). Такая программа при чтении стандартного входного потока могла бы использовать фрагмент, подобный следующему:</p>
    <p><code>#include &lt;errno.h&gt;</code></p>
    <p><code>extern int errno;</code></p>
    <empty-line/>
    <p><code>...</code></p>
    <p><code>if (read(0, &amp;c, 1) &lt;= 0) /* EOF или прерывание */</code></p>
    <p><code> if (errno == EINTR) { /* EOF, вызванный прерыванием */</code></p>
    <p><code>  errno = 0; /* устанавливается для следующего раза */</code></p>
    <p><code> } else { /* настоящий конец файла */</code></p>
    <p><code>  ...</code></p>
    <p><code> }</code></p>
    <p>Очень сложно постоянно следить за тем, как реакция на сигнал комбинируется с выполнением других программ. Предположим, программа ловит сигналы прерывания и располагает средствами (типа "<code>!</code>"в <code>ed</code>) для выполнения других программ. Тогда программа могла бы выглядеть так:</p>
    <p><code>if (fork() == 0)</code></p>
    <p><code> execlp(...);</code></p>
    <p><code>signal(SIGINT, SIG_IGN); /* родитель игнорирует прерывание */</code></p>
    <p><code>wait(&amp;status); /* пока потомок не завершился */</code></p>
    <p><code>signal(SIGINT, onintr); /* восстанавливает прерывания */</code></p>
    <p>Почему? Сигналы посылаются всем вашим процессам. Предположим, программа, которую вы вызвали, ловит свои собственные сигналы прерывания, как это делает редактор. Если вы прервете выполнение подпрограммы, она получит сигнал, вернется к своему главному циклу и, возможно, начнет читать с вашего терминала. Но вызывающая программа также перейдет от <code>wait</code> к подпрограмме и будет читать с терминала. Два процесса, читающие с вашего терминала, создадут трудную ситуацию, так как в результате системе придется гадать, к кому попадет та или иная строка входного потока. Решение состоит в том, чтобы родительская программа игнорировала прерывания, пока не завершился процесс-потомок. Это решение нашло свое отражение при обработке сигнала в <code>system</code>:</p>
    <p><code>#include &lt;signal.h&gt;</code></p>
    <empty-line/>
    <p><code>system(s) /* run command line s */</code></p>
    <p><code> char *s;</code></p>
    <p><code>{</code></p>
    <p><code> int status, pid, w, tty;</code></p>
    <p><code> int (*istat)(), (*qstat)();</code></p>
    <empty-line/>
    <p><code> ...</code></p>
    <p><code> if ((pid = fork()) == 0) {</code></p>
    <p><code>  ...</code></p>
    <p><code>  execlp("sh", "sh", "-c", s, (char*)0);</code></p>
    <p><code>  exit(127);</code></p>
    <p><code> }</code></p>
    <p><code> ...</code></p>
    <p><code> istat = signal(SIGINT, SIG_IGN);</code></p>
    <p><code> qstat = signal(SIGQUIT, SIG_IGN);</code></p>
    <p><code> while ((w = wait(&amp;status)) != pid &amp;&amp; w != -1);</code></p>
    <p><code> if (w == -1)</code></p>
    <p><code>  status = -1;</code></p>
    <p><code> signal(SIGINT, istat);</code></p>
    <p><code> signal(SIGQUIT, qstat);</code></p>
    <p><code> return status;</code></p>
    <p><code>}</code></p>
    <p>Несколько слов по поводу описаний: функция <code>signal</code>, очевидно, имеет довольно странный второй аргумент. Фактически он представляет собой указатель на функцию, поставляющую целое значение, и в то же время это тип самой подпрограммы сигнала. Две величины, <code>SIG_IGN</code> и <code>SIG_DFL</code>, имеют правильный тип, но выбраны так, что не совпадают ни с одной из существующих функции. Для любознательных покажем, как они определены для PDP-11 и VAX: определения, видимо, достаточно "неуклюжи", чтобы стимулировать использование <code>&lt;signal.h&gt;</code>.</p>
    <p><code>#define SIG_DFL (int(*)())0</code></p>
    <p><code>#define SIG_IGM (int(*)())1</code></p>
    <subtitle>Будильники</subtitle>
    <p>Системный вызов <code>alarm(n)</code> обеспечивает посылку сигнала <code>SIGALRM</code> вашему процессу через n секунд. Сигнал будильника может быть использован для того, чтобы удостовериться в возникновении каких-то событий за соответствующее время. Если что-нибудь произошло, сигнал будильника может быть выключен; в противном случае процесс может получить управление, перехватив этот сигнал.</p>
    <p>Для иллюстрации приведем программу <code>timeout</code>, которая запускает другую команду; если команда не закончила свое выполнение за определенное время, она будет завершена по звонку будильника. Например, вспомните команду <code>watchfor</code> из гл. 5. Вместо того чтобы запускать ее без ограничения времени работы, установите ограничение в часах:</p>
    <p><code>$ timeout -3600 watchfor dmg &amp;</code></p>
    <p>Программа <code>timeout</code> демонстрирует почти все возможности, которые мы обсуждали в последних двух разделах. Создан процесс-потомок, родительский процесс устанавливает будильник и затем ждет, пока потомок завершит работу. Если будильник "зазвенел" первым, потомок уничтожается. Делается попытка вернуть состояние потомка при выходе.</p>
    <p><code>/* timeout: set time limit on a process */</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#include &lt;signal.h&gt;</code></p>
    <empty-line/>
    <p><code>int pid; /* child process id */</code></p>
    <p><code>char *progname;</code></p>
    <empty-line/>
    <p><code>main(argc, argv)</code></p>
    <p><code> int argc;</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> int sec = 10, status, onalarm();</code></p>
    <empty-line/>
    <p><code> progname = argv[0];</code></p>
    <p><code> if (argc &gt; 1 &amp;&amp; argv[1][0] == '-') {</code></p>
    <p><code>  sec = atoi(&amp;argv[1][1]);</code></p>
    <p><code>  argc--;</code></p>
    <p><code>  argv++;</code></p>
    <p><code> }</code></p>
    <p><code> if (argc &lt; 2)</code></p>
    <p><code>  error("Usage: %s [-10] command", progname);</code></p>
    <p><code> if ((pid=fork()) == 0) {</code></p>
    <p><code>  execvp(argv[1], &amp;argv[1]);</code></p>
    <p><code>  error("couldn't start %s", argv[1]);</code></p>
    <p><code> }</code></p>
    <p><code> signal(SIGALRM, onalarm);</code></p>
    <p><code> alarm(sec);</code></p>
    <p><code> if (wait(&amp;status) == -1 || (status &amp; 0177) != 0)</code></p>
    <p><code>  error("%s killed", argv[1]);</code></p>
    <p><code> exit((status &gt;&gt; 8) &amp; 0377);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>onalarm() /* kill child when alarm arrives */</code></p>
    <p><code>{</code></p>
    <p><code> kill(pid, SIGKILL);</code></p>
    <p><code>}</code></p>
    <cite>
     <subtitle>Упражнение 7.18</subtitle>
     <p>Можете ли вы представить, как реализована <code>sleep</code>? <emphasis>Подсказка</emphasis>: <code>pause(2)</code>. При каких обстоятельствах (если это вообще возможно) <code>sleep</code> и <code>alarm</code> могли бы помешать друг другу?</p>
    </cite>
    <cite>
     <subtitle>Историческая и библиографическая справка</subtitle>
     <p>Детального описания реализации системы UNIX не существует отчасти потому, что программа является собственностью фирмы. В статье К. Томпсона "UNIX implementation" (BSTJ, July, 1978) описываются основные идеи. Другие статьи, в которых обсуждаются связанные с UNIX темы, это "The UNIX system — a retrospective" (BSTJ, July, 1978) и "The evolution of the UNIX timesharing system" (Symposium on Language Design and Programming Methodology, Springer — Verlag, Lecture Notes in Computer Science #79, 1979). Обе статьи принадлежат Д. Ритчи.</p>
     <p>Программа <code>readslow</code> была разработана П. Вейнбергером как недорогое средство, позволяющее следить за успехами шахматной машины "Белла" К. Томпсона и Дж. Кондона во время шахматных турниров. "Белла" записывала позиции своей игры в файл, а зрители просматривали файл с помощью <code>readslow</code>, не отнимая драгоценного времени у машины. (Новейшая версия "Беллы" лишь небольшую долю вычислений выполняет на основной машине, так что проблема снята.)</p>
     <p>На создание <code>spname</code> нас вдохновил Т. Дафф. Статья А. Дархема, Д. Лэмба и Дж. Сакса "Spelling correction in user interfaces" (CACM, October, 1983) представляет иной способ коррекции написания в контексте программы почты.</p>
    </cite>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 8</p>
    <p>Разработка программ</p>
   </title>
   <section>
    <p>Первоначально системе UNIX предназначалась роль среды для разработки программ. В настоящей главе мы обсудим некоторые применяемые с этой целью программные средства на примере солидной программы — интерпретатора языка программирования, сравнимого по мощности с Бейсиком. Мы выбрали реализацию языка, потому что возникающие здесь проблемы характерны для больших программ. Более того, многие программы полезно рассматривать как языковые процессоры, преобразующие входной поток определенной структуры в последовательность действий и выходной поток, т. е. мы хотим продемонстрировать вам программные средства разработки языков.</p>
    <p>В частности, вашему вниманию предлагаются следующие программы:</p>
    <p>• <code>yacc</code> — генератор синтаксических анализаторов; программа, которая по описанию грамматики языка порождает анализатор;</p>
    <p>• <code>make</code> — программа, определяющая процесс компиляции сложных программ и управляющая им;</p>
    <p>• <code>lex</code> — программа, аналогичная <code>yacc</code>, но создающая лексические анализаторы.</p>
    <p>Мы покажем вам приемы разработки программ в несколько этапов — от простого к сложному. Ниже описаны шесть этапов реализации языка, каждый из которых поучителен уже сам по себе. Эти этапы отражают реальный процесс написания программы:</p>
    <p>1. Создание калькулятора с четырьмя действиями: <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code> (и со скобками). Калькулятор выполняет операции над числами с плавающей точкой, каждая строка состоит из одного выражения; полученное значение печатается сразу.</p>
    <p>2. Добавление переменных с именами от <code>а</code> до <code>z</code>. В этой версии есть также унарный минус и некоторые средства защиты от ошибок.</p>
    <p>3. Добавление переменных с именами произвольной длины, встроенных функций для <code>sin</code>, <code>exp</code> и т.п., полезных констант типа &#960; (обозначается как <code>PI</code>) и операции возведения в степень.</p>
    <p>4. Внесение внутренних изменений: оператор вычисляется не непосредственно, а порождает код, который впоследствии интерпретируется. Новые возможности не добавляются, но подготавливается переход к п. 5.</p>
    <p>5. Добавление структур управления: <code>if-else</code> и <code>while</code> — группирование операторов с помощью и и операции отношений типа <code>&gt;</code>, <code>&lt;=</code> и т.п.</p>
    <p>6. Добавление рекурсивных процедур и функций с параметрами, а также операторов для ввода-вывода строк и чисел.</p>
    <p>Окончательная версия языка описана в гл. 9 как пример программных средств подготовки документации системы UNIX. В приложении 2 приводится справочное руководство по калькулятору.</p>
    <p>Эта глава довольно объемная, поскольку в ней детально рассматривается, как правильно написать нетривиальную программу. Предполагается, что вы знаете язык Си и имеете под рукой экземпляр справочного руководства по системе UNIX (том 2), поскольку просто невозможно объяснить все нюансы. Будьте готовы к тому, что вам придется прочитать главу несколько раз. Окончательная версия полностью представлена в приложении 3. Заметим, кстати, что мы долго спорили из-за имени языка, но так и не придумали подходящее. Остановились на <code>hoc</code>, что означает "калькулятор высокого уровня" (high level calculator).</p>
    <p>Его версии соответственно называются <code>hoc1</code>, <code>hoc2</code> и т.д.</p>
   </section>
   <section>
    <title>
     <p>8.1 Этап 1: калькулятор с четырьмя действиями</p>
    </title>
    <p>Прежде всего рассмотрим реализацию <code>hoc1</code> — программы с такими же возможностями, как и простейший карманный калькулятор, но гораздо менее удобной для переноса. Она выполняет четыре операции: <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code> и, имеет скобки с произвольной глубиной вложенности, чем обладают лишь немногие калькуляторы. Если вы введете выражение и символ <emphasis>RETURN</emphasis>, результат будет напечатан в следующей строке:</p>
    <p><code>$ hoc1</code></p>
    <p><code>4*3*2</code></p>
    <p><code>  24</code></p>
    <p><code>(1+2)*(3+4)</code></p>
    <p><code> 21</code></p>
    <p><code>1/2</code></p>
    <p><code> 0.5</code></p>
    <p><code>355/113</code></p>
    <p><code> 3.1415929</code></p>
    <p><code>-3 - 4</code></p>
    <p><code>hoc1 : syntax error near line 4 No unary minus yet</code></p>
    <p><code>$</code></p>
    <subtitle>Грамматика</subtitle>
    <p>С появлением формы Бэкуса-Наура, предложенной для Алгола, языки стали описываться с помощью формальной грамматики. Абстрактное описание грамматики <code>hoc1</code> простое и краткое:</p>
    <p><code>список: выраж \n</code></p>
    <p><code> список выраж \n</code></p>
    <p><code>выраж: NUMBER</code></p>
    <p><code> выраж + выраж</code></p>
    <p><code> выраж - выраж</code></p>
    <p><code> выраж * выраж</code></p>
    <p><code> выраж / выраж</code></p>
    <p><code> ( выраж )</code></p>
    <p>Здесь <emphasis>список</emphasis> — последовательность выражений, каждое из которых завершается символом перевода строки, а <emphasis>выражение</emphasis> — число или пара выражений, объединенных операцией, либо выражение в скобках.</p>
    <p>Приведенное описание не полное, так как в нем не определены естественный приоритет и ассоциативность операций, а также не заданы значения конструкциям языка. Хотя список специфицируется через <emphasis>выраж</emphasis>, а оно в свою очередь через <code>NUMBER</code>, само <code>NUMBER</code> нигде не определено, Поэтому чтобы перейти от упрощенного описания к работающей программе, необходимо внести ясность в эти вопросы.</p>
    <subtitle>Программа <code>yacc</code></subtitle>
    <p>Генератор синтаксических анализаторов <code>yacc</code><a l:href="#n15" type="note">[15]</a> преобразует компилятор грамматических правил языка, подобных приведенным выше, в анализатор, который разбирает операторы языка. <code>Yacc</code> обладает возможностью приписывать значения компонентам грамматики таким образом, что в процессе разбора значение может быть "вычислено" . Используется <code>yacc</code> поэтапно,</p>
    <p>На первом этапе записывается грамматика языка, но более точно, чем было показано ранее, т.е. определяется синтаксис. На этом этапе назначение <code>yacc</code> — предупреждение появления ошибок и двусмысленностей в грамматике.</p>
    <p>На втором этапе каждое правило (правило вывода грамматики) сопровождается описанием действия на тот случай, когда найден экземпляр грамматической конструкции в разбираемой программе. Часть действия записывается на Си, причем должны выполняться определенные соглашения о связи между грамматикой и текстом. Здесь определяется семантика языка.</p>
    <p>Третий этап — создание лексического анализатора, который должен читать разбираемый входной поток и разбивать его для анализатора на осмысленные единицы. Примером лексической единицы длиной в несколько символов может служить <code>NUMBER</code>; операции из одного символа, такие, как <code>+</code> и <code>*</code>, также являются лексическими единицами. По традиции лексические единицы называют лексемами.</p>
    <p>На следующем этапе разрабатывается управляющая процедура, которая вызывает анализатор, созданный <code>yacc</code>.</p>
    <p>Программа <code>yacc</code> преобразует грамматику и семантические процедуры в функцию разбора с именем <code>yyparse</code> и записывает ее в виде файла с текстом на Си. Если <code>yacc</code> не находит ошибок, то анализатор, лексический анализатор и управляющую процедуру можно откомпилировать, возможно, связать с другими программами на Си и выполнить.</p>
    <p>Действие <code>yacc</code> сводится к многократному обращению к лексическому анализатору за лексемами, распознаванию грамматических (синтаксических) конструкций во входном потоке и выполнению семантических процедур по мере распознавания грамматических правил. Вызывать лексический анализатор нужно по имени <code>yylex</code>, так как именно эту функцию инициирует анализатор <code>yyparse</code> всякий раз, когда ему нужна следующая лексема. (Все имена, используемые <code>yacc</code>, начинаются с <code>y</code>.)</p>
    <p>Чтобы быть более точными, укажем, что входной поток для <code>yacc</code> должен иметь такой вид:</p>
    <p><code>%{</code></p>
    <p><code> Операторы Си типа #include, описания и т. д.</code></p>
    <p><code> Эта часть необязательна.</code></p>
    <p><code>%}</code></p>
    <p><code>yacc-описания: лексемы, грамматические переменные,</code></p>
    <p><code>информация о приоритетах и ассоциативности</code></p>
    <p><code>%%</code></p>
    <p><code>грамматические правила и действия</code></p>
    <p><code>%%</code></p>
    <p><code>еще операторы Си (необязательно):</code></p>
    <p><code>main() {</code></p>
    <p><code> ...; yyparse(); ...</code></p>
    <p><code>}</code></p>
    <p><code>yylex() {</code></p>
    <p><code> ...</code></p>
    <p><code>}</code></p>
    <p><code>...</code></p>
    <p>Этот поток поступает на вход <code>yacc</code>, а результат записывается в файл <code>y.tab.c</code>, имеющий следующую структуру:</p>
    <p><code>Операторы на Си между %{ и %}, если есть</code></p>
    <p><code>Операторы на Си из части после второй комбинации %%, если есть:</code></p>
    <p><code>main() {</code></p>
    <p><code> ...; yyparse(); ...</code></p>
    <p><code>}</code></p>
    <p><code>yylex() {</code></p>
    <p><code> ...</code></p>
    <p><code>}</code></p>
    <p><code>...</code></p>
    <p><code>yyparse() {</code></p>
    <p><code> анализатор, который вызывает yylex()</code></p>
    <p><code>}</code></p>
    <p>Такой подход типичен для системы UNIX: <code>yacc</code> выдает текст на Си, а не оттранслированный файл (<code>.o</code>), что является наиболее гибким решением, так как созданный текст, переносим и легко поддается любому другому преобразованию (если появится хорошая идея).</p>
    <p>Генератор <code>yacc</code> сам по себе представляется мощным программным средством. Его изучение потребует от вас, конечно, некоторых усилий, но все ваши "затраты" многократно окупятся. Анализаторы, создаваемые <code>yacc</code>, — небольшие, эффективные и корректные (хотя за семантические преобразования отвечаете вы). Кроме того, многие неприятные проблемы, связанные с процессом разбора, решаются автоматически. Программы языковых распознавателей достаточно легко создавать и, что, возможно, еще более важно, изменять по мере совершенствования определения языка.</p>
    <subtitle>Использование программ на этапе 1</subtitle>
    <p>Исходный текст <code>hoc1</code> состоит из грамматических правил с описанием действий лексической процедуры <code>yylex</code> и функции <code>main</code>, хранимых в одном файле <code>hoc.y</code>. (Имена файлов, содержащих текст для <code>yacc</code>, традиционно оканчиваются на <code>.y</code>, но это соглашение в отличие от соглашения о <code>сс</code> и .c не поддерживает сам <code>yacc</code>.) Грамматика составляет первую половину файла <code>hoc.y</code>:</p>
    <p><code>$ cat hoc.y</code></p>
    <p><code>%{</code></p>
    <p><code>#define YYSTYPE double /* data type of yacc stack */</code></p>
    <p><code>%}</code></p>
    <p><code>%token NUMBER</code></p>
    <p><code>%left '+' /* left associative, same precedence */</code></p>
    <p><code>%left '*' '/' /* left assoc., higher precedence */</code></p>
    <p><code>%%</code></p>
    <p><code>list: /* nothing */</code></p>
    <p><code> | list '\n'</code></p>
    <p><code> | list expr '\n' { printf("\t%.8g\n", $2); }</code></p>
    <p><code> ;</code></p>
    <p><code>expr: NUMBER { $$ = $1; }</code></p>
    <p><code> | expr '+' expr { $$ = $1 + $3; }</code></p>
    <p><code> | expr '-' expr { $$ = $1 - $3; }</code></p>
    <p><code> | expr '*' expr { $$ = $1 * $3; }</code></p>
    <p><code> | expr '/' expr { $$ = $1 / $3; }</code></p>
    <p><code> | '(' expr ')' { $$ = $2; }</code></p>
    <p><code> ;</code></p>
    <p><code>%%</code></p>
    <p><code>/* end of grammar */</code></p>
    <p><code>...</code></p>
    <p>Вы видите, как много информации заключено в этих нескольких строках. Поскольку мы не можем вам здесь все объяснить, в частности, как работает синтаксический анализатор, обратитесь к справочному руководству по <code>yacc</code>.</p>
    <p>Альтернативные правила разделены символом <code>'|'</code>. С каждым грамматическим правилом может быть связано определенное действие, которое выполняется, когда экземпляр этого правила распознается во входном потоке. Действие описывается последовательностью операторов Си, заключенной в фигурные скобки. Внутри последовательности <code>$n</code> (т.е. <code>$1</code>, <code>$2</code> и т.д.) определяет значение, вырабатываемое <code>n</code>-м компонентом правила, а <code>$$</code> значение, вырабатываемое всеми компонентами правила в целом. Так, в правиле</p>
    <p><code>expr: NUMBER { $$ = $1; }</code></p>
    <p><code>$1</code> — значение, вырабатываемое при распознавании <code>NUMBER</code>, и оно же является результирующим значением <code>expr</code>. В данном случае присваивание <code>$$ = $1</code> может быть опущено, так как <code>$$</code> всегда принимает значение <code>$1</code> (если не устанавливается явно каким либо иным образом). В следующей строке с правилом</p>
    <p><code>expr: expr '+' expr { $$ = $1 + $3; }</code></p>
    <p>результирующее значение <code>expr</code> является суммой двух компонентов, тоже <code>expr</code>. Отметим, что <code>$2</code> соответствует <code>'+'</code> т.е. каждый компонент пронумерован.</p>
    <p>Строкой выше выражение, за которым следует символ перевода строки (<code>'\n'</code>), распознается как <emphasis>список</emphasis>, и печатается его значение. Если за такой конструкцией следует конец входного потока, процесс разбора завершается правильно. Список может быть пустой строкой; так учитываются пустые входные строки.</p>
    <p>Формат входного потока для <code>yacc</code> — произвольный. Наш формат рекомендуется как стандартный.</p>
    <p>В этой реализации процесс распознавания или разбора входного потока приводит к немедленному вычислению выражения. В более сложных решениях (включая <code>hoc4</code> и его последующие версии) процесс разбора порождает код для дальнейшего выполнения.</p>
    <p>Наглядно представить разбор вам поможет рис. 8.1, где изображено дерево разбора. Кроме того, вы должны знать, как вычисляются значения и как они распространяются от листьев к корню дерева.</p>
    <empty-line/>
    <image l:href="#img_4.jpeg"/>
    <p><emphasis>Рис. 8.1</emphasis>: Дерево разбора для 2 + 3*4</p>
    <empty-line/>
    <p>Реально значения не полностью разобранных правил хранятся в стеке и через стек передаются от одного правила к следующему. Обычно данные в стеке имеют целый тип, но поскольку мы в своей работе используем числа с плавающей точкой, необходимо переопределить значение по умолчанию. Определение</p>
    <p><code>#define YYSTYPE double</code></p>
    <p>устанавливает двойную точность для типа данных стека.</p>
    <p>Теперь перейдем к описанию синтаксических классов, распознаваемых лексическим анализатором, если только они не являются литералами, состоящими из одного символа вида <code>'+'</code> и <code>'-'</code>. Описание <code>%token</code> специфицирует одни или несколько таких объектов. При необходимости можно задать левую или правую ассоциативность, используя <code>%left</code> или <code>%right</code> вместо <code>%token</code>.</p>
    <p>(Левая ассоциативность означает, что <code>a-b-с</code> будет разбираться как <code>(а - b) - с</code>, а не <code>а - (b - с)</code>.) Приоритет устанавливается порядком появления операции: лексемы из одного определения имеют один и тот же приоритет, а лексемы, специфицированные позднее, — более высокий. Таким образом, в грамматике может быть неоднозначность (т.е. для некоторых входных потоков существует несколько способов разбора), но дополнительная информация в определениях разрешает эту неоднозначность.</p>
    <p>Вторую половину файла <code>hoc.y</code> составляют процедуры:</p>
    <p><code>/* Продолжение hoc.y */</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#include &lt;ctype.h&gt;</code></p>
    <empty-line/>
    <p><code>char *progname; /* for error messages */</code></p>
    <p><code>int lineno = 1;</code></p>
    <empty-line/>
    <p><code>main(argc, argv) /* hoc1 */</code></p>
    <p><code>char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> progname = argv[0];</code></p>
    <p><code> yyparse();</code></p>
    <p><code>}</code></p>
    <p>Функция main обращается к <code>yyparse</code> для разбора входного потока. Переход в цикле от одного выражения к другому происходит в рамках грамматики с помощью последовательности правил вывода для <emphasis>списка</emphasis>. Приемлемо также обращаться в цикле к <code>yyparse</code> из функции <code>main</code>, если действия для списка предполагают печать значения и немедленный возврат.</p>
    <p>Функция <code>yyparse</code> в свою очередь многократно обращается за лексемами из входного потока к функции <code>yylex</code>. Наша функция <code>yylex</code> проста: в ее задачи входят пропуск пробелов и символов табуляции, преобразование цифровых строк в числовое значение и подсчет входных строк для вывода сообщений об ошибках. Поскольку грамматика допускает только <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>(</code>, <code>)</code> и <code>\n</code>, при появлении любого другого символа <code>yyparse</code> выдает сообщение об ошибке. Получение 0 означает для <code>yyparse</code> "конец файла".</p>
    <p><code>/* Продолжение hoc.y */</code></p>
    <empty-line/>
    <p><code>yylex() /* hoc1 */</code></p>
    <p><code>{</code></p>
    <p><code> int с;</code></p>
    <p><code> while ((c=getchar()) == ' ' || с == '\t')</code></p>
    <p><code>  ;</code></p>
    <p><code> if (c == EOF)</code></p>
    <p><code>  return 0;</code></p>
    <p><code> if (c == '.' || isdigit(c)) {</code></p>
    <p><code>  /* number */</code></p>
    <p><code>  ungetc(c, stdin);</code></p>
    <p><code>  scanf("%lf", &amp;yylval);</code></p>
    <p><code>  return NUMBER;</code></p>
    <p><code> }</code></p>
    <p><code> if (c == '\n')</code></p>
    <p><code>  lineno++;</code></p>
    <p><code> return с;</code></p>
    <p><code>}</code></p>
    <p>Переменная <code>yylval</code> используется для связи между синтаксическим и лексическим анализаторами; она определена в <code>yyparse</code> и имеет тот же тип, что стек <code>yacc</code>. Функция <code>yylex</code> возвращает <emphasis>тип</emphasis> лексемы, равно как и ее функциональное значение, и приравнивает <code>yylval</code> <emphasis>значению</emphasis> лексемы (если оно есть). Например, число с плавающей точкой имеет тип <code>NUMBER</code> и значение, скажем, 12.34. Для некоторых лексем, прежде всего состоящих из одного символа, таких, как <code>'+'</code> или <code>'\n'</code>, в грамматике используется только тип. В этом случае <code>yylval</code> не нужно определять.</p>
    <p>Определение <code>%token NUMBER</code> из входного файла для <code>yacc</code> преобразуется в оператор <code>#defin</code>e в выходном файле <code>y.tab.c</code>, поэтому <code>NUMBER</code> можно использовать в качестве константы в любом месте Си программы. <code>Yacc</code> выбирает такие значения, которые не будут смешиваться с символами ASCII.</p>
    <p>При наличии синтаксической ошибки <code>yyparse</code> обращается к <code>yyerror</code> со строкой, содержащей загадочное сообщение: "syntax error" ("синтаксическая ошибка"). Предполагается, что функцию <code>yyerror</code> предоставляет пользователь: в нашей функции строка просто передается другой функции — <code>warning</code>, которая выдает некоторую дополнительную информацию. В последующих версиях <code>hoc</code> функция <code>warning</code> будет применяться непосредственно.</p>
    <p><code>yyerror(s) /* called for yacc syntax error */</code></p>
    <p><code> char *s;</code></p>
    <p><code>{</code></p>
    <p><code> warning(s, (char*)0);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>warning(s, t) /* print warning message */</code></p>
    <p><code> char *s, *t;</code></p>
    <p><code>{</code></p>
    <p><code> fprintf(stderr, "%s: %s", progname, s);</code></p>
    <p><code> if (t)</code></p>
    <p><code>  fprintf(stderr, " %s", t);</code></p>
    <p><code> fprintf(stderr, " near line %d\n", lineno);</code></p>
    <p><code>}</code></p>
    <p>Этим завершаются процедуры файла <code>hoc.y</code>. Трансляция программы для <code>yacc</code> происходит в два этапа:</p>
    <p><code>$ yacc hoc.y         </code>Выходной поток попадает в y.tab.c</p>
    <p><code>$ сс y.tab.c -о hoc1 </code>Выполняемая программа попадает в hoc1</p>
    <p><code>$ hoc1</code></p>
    <p><code>2/3</code></p>
    <p><code> 0.66666667</code></p>
    <p><code>-3-4</code></p>
    <p><code>hoc1: syntax error near line 1</code></p>
    <p><code>$</code></p>
    <cite>
     <subtitle>Упражнение 8.1</subtitle>
     <p>Исследуйте структуру файла <code>y.tab.c</code> (для <code>hoc1</code> это составляет около 300 строк текста).</p>
    </cite>
    <subtitle>Внесение изменений — унарный минус</subtitle>
    <p>Ранее мы утверждали, что, работая с <code>yacc</code>, легко менять язык. В качестве примера добавим к <code>hoc1</code> унарный минус, чтобы выражения типа</p>
    <p><code>-3-4</code></p>
    <p>вычислялись, а не отвергались как синтаксические ошибки. Всего две строки нужно дополнительно включить в <code>hoc.y</code>. Добавляется новая лексема <code>UNARYMINUS</code> в ту часть грамматики, где задаются приоритеты, чтобы унарный минус имел наивысший приоритет:</p>
    <p><code>%left '+' '-'</code></p>
    <p><code>%left '*' '/'</code></p>
    <p><code>%left UNARYMINUS /* новая лексема */</code></p>
    <p>Грамматика увеличивается на одно правило для <code>expr</code>:</p>
    <p><code>expr: NUMBER ($$= $1;}</code></p>
    <p><code> | '-' expr %prec UNARYMINUS {$$=- $2} /* новое */</code></p>
    <p>Определение <code>%prec</code> "говорит", что символ унарного минуса (т.е. знак <code>"-"</code> перед выражением) имеет тот же приоритет, что и <code>UNARYMINUS</code> (наивысший); действие заключается в изменении знака. Приоритет минуса между двумя выражениями устанавливается по умолчанию.</p>
    <cite>
     <subtitle>Упражнение 8.2</subtitle>
     <p>Добавьте операции <code>%</code> (взятие остатка) и унарный плюс к <code>hoc1</code>. Рекомендация: обратитесь к справочному руководству по <code>frexp(3)</code>.</p>
    </cite>
    <subtitle>Некоторые замечания относительно <code>make</code></subtitle>
    <p>Обидно, что приходится вводить две команды для компиляции <code>hoc1</code>. Хотя, конечно, нетрудно составить командный файл для такого задания, но есть лучший способ, который позднее можно распространить на тот случай, когда программа состоит из нескольких исходных файлов. Программа <code>make</code> читает описания взаимозависимости компонентов программы и позволяет создать ее действующую версию. Она проверяет время последней модификации каждого компонента, выясняет минимальный объем перекомпиляции, которую необходимо выполнить для получения новой действующей версии, и затем запускает процесс. Программа <code>make</code> разбирается в запутанных многошаговых процессах, в частности в <code>yacc</code>, поэтому ей можно давать задания, не уточняя отдельные шаги.</p>
    <p>Особенно полезно обращаться к <code>make</code>, когда создаваемая программа настолько велика, что "располагается" в нескольких исходных файлах. Однако она удобна и для таких малых программ, как <code>hoc1</code>. Ниже приведены описания команд для <code>make</code>, рассчитанные на <code>hoc1</code>, которые <code>make</code> предполагает найти в файле с именем <code>makefile</code>.</p>
    <p><code>$ cat makefile</code></p>
    <p><code>hoc1: hoc.o</code></p>
    <p><code>cc hoc.o -o hoc1</code></p>
    <p><code>$</code></p>
    <p>Здесь сообщается, что <code>hoc1</code> зависит от <code>hoc.o</code> и что <code>hoc1</code> создается из <code>hoc.o</code> с помощью команды <code>сс</code>, которая запускает компилятор Си, помещая выходной поток в файл <code>hoc1</code>. Программа <code>make</code> уже "знает", как преобразовать входной файл для <code>yacc</code> <code>hoc.y</code> в выходной файл <code>hoc.o</code>:</p>
    <p><code>$ make </code>Проделаем первый раз получение hoc1 с помощью make</p>
    <p><code>yacc hoc.y</code></p>
    <p><code>сс -с y.tab.c</code></p>
    <p><code>rm y.tab.c</code></p>
    <p><code>mv y.tab.o hoc.о</code></p>
    <p><code>сс hoc.о -о hoc1</code></p>
    <p><code>$ make </code>Попробуем еще раз</p>
    <p><code>'hoc1' is up to date make понимает, что это не нужно</code></p>
    <p><code>$</code></p>
   </section>
   <section>
    <title>
     <p>8.2 Этап 2: переменные и восстановление после ошибки</p>
    </title>
    <p>Следующий шаг переход от <code>hoc1</code> к <code>hoc2</code>, который сводится к расширению памяти (в памяти хранится 26 переменных с именами от <code>а</code> до <code>z</code>). Это довольно несложный и весьма полезный промежуточный этап. Мы также введем здесь процесс обработки ошибок. Если вы проверите <code>hoc1</code>, то убедитесь, что реакцией на синтаксические ошибки являются вывод сообщения и прекращение работы. Поведение же <code>hoc1</code> в случае арифметических ошибок типа деления на нуль достойно всяческого порицания:</p>
    <p><code>$ hoc1</code></p>
    <p><code>1/0</code></p>
    <p><code>Floating exception - core dump</code></p>
    <p><code>$</code></p>
    <p>Для реализации новых возможностей требуются лишь небольшие изменения: приблизительно 35 строк текста. Лексический анализатор <code>yylex</code> должен распознавать буквы как переменные, а грамматика содержать правила вывода вида</p>
    <p><code>expr: VAR</code></p>
    <p><code> | VAR '=' expr</code></p>
    <p>Выражение может содержать операцию присваивания; разрешены также многократные присваивания типа</p>
    <p><code>x = y = z = 0</code></p>
    <p>Простейший способ хранения значений переменных создать массив из 26 элементов; однобуквенную переменную можно использовать в качестве индекса массива. Однако если анализатору предстоит обрабатывать и имена переменных, и значения в одном стеке, необходимо сообщить <code>yacc</code>, что элемент стека является объединением <code>double</code> и <code>int</code>, а не просто элементом типа <code>double</code>. Это делается с помощью описания <code>%union</code>. Описания <code>#define</code> или <code>typedef</code> подходят для определения стека из базовых типов как <code>double</code>, но для типов объединения требуется описание <code>%union</code>, поскольку <code>yacc</code> осуществляет контроль типов в выражениях вида <code>$$ = $2</code>.</p>
    <p>Ниже приведена часть определения грамматики <code>hoc.y</code> для программы <code>hoc2</code>:</p>
    <p><code>$ cat hoc.y</code></p>
    <p><code>%{</code></p>
    <p><code>double mem[26]; /* memory for variables 'a'..'z' */</code></p>
    <p><code>%}</code></p>
    <p><code>%union {     /* stack type */</code></p>
    <p><code> double val; /* actual value */</code></p>
    <p><code> int index;  /* index into mem[] */</code></p>
    <p><code>}</code></p>
    <p><code>%token &lt;val&gt; NUMBER</code></p>
    <p><code>%token &lt;index&gt; VAR</code></p>
    <p><code>%type &lt;val&gt; expr</code></p>
    <p><code>%right '='</code></p>
    <p><code>%left '+'</code></p>
    <p><code>%left '*' '/'</code></p>
    <p><code>%left UNARYMINUS</code></p>
    <p><code>%%</code></p>
    <p><code>list: /* nothing */</code></p>
    <p><code> | list '\n'</code></p>
    <p><code> | list expr '\n' { printf ("\t%.8g\n", $2); }</code></p>
    <p><code> | list error '\n' { yyerrok; }</code></p>
    <p><code> ;</code></p>
    <p><code>expr: NUMBER</code></p>
    <p><code> | VAR { $$ = mem[$1]; }</code></p>
    <p><code> | VAR '=' expr { $$ = mem[$1] = $3; }</code></p>
    <p><code> | expr '+' expr { $$ = $1 + $3; }</code></p>
    <p><code> | expr '-' expr { $$ = $1 - $3; }</code></p>
    <p><code> | expr '*' expr { $$ = $1 * $3; }</code></p>
    <p><code> | expr '/' expr {</code></p>
    <p><code>  if ($3 == 0.0)</code></p>
    <p><code>  execerror("division by zero", "");</code></p>
    <p><code>  $$ = $1 / $3;</code></p>
    <p><code> }</code></p>
    <p><code> | '(' expr ')' { $$ = $2; }</code></p>
    <p><code> | '-' expr %prec UNARYMINUS { $$ = -$2; }</code></p>
    <p><code> ;</code></p>
    <p><code>%%</code></p>
    <p><code>/* end of grammar */</code></p>
    <p><code>...</code></p>
    <p>Из описания <code>%union</code> следует, что элементы стека содержат или число с двойной точностью (обычный случай), или целое, являющееся индексом в массиве <code>mem</code>. В описании <code>%token</code> дополнительно указывается тип значения. В описании <code>%type</code> есть сведения о том, что <emphasis>выраж</emphasis> является элементом объединения <code>&lt;val&gt;</code>, т.е. <code>double</code>. Информация о типе позволяет <code>yacc</code> обращаться к нужному элементу объединения. Обратите внимание: <code>"="</code> представляет собой правоассоциативную операцию, тогда как другие операции — левоассоциативные.</p>
    <p>Обработка ошибок происходит в несколько этапов. Прежде всего производится проверка на нулевой делитель: если делитель равен нулю, вызывается процедура обработки ошибок <code>execerror</code>. Второй этап заключается в перехвате сигнала "переполнение вещественного" ("floating point exception"), который возникает при переполнении вещественного числа. Сигнал устанавливается в функции <code>main</code>. Последний шаг восстановления после ошибки заключается в добавлении к грамматике правила вывода для ошибки. В грамматике <code>yacc</code> слово <code>error</code> зарезервировано; оно дает возможность анализатору осознать синтаксическую ошибку и восстановиться после нее. Если произойдет ошибка, <code>yacc</code> в конце концов использует это правило, распознает ошибку как грамматически "правильную" конструкцию и, таким образом, восстановится. Действие <code>yyerrok</code> заключается в установке признака в анализаторе, который позволяет вернуться ему назад в состояние осмысленного разбора. Восстановление после ошибки сложная проблема для всех анализаторов. Мы показали вам здесь лишь самые элементарные приемы и только обозначили возможности <code>yacc</code>.</p>
    <p>В грамматике <code>hoс2</code> произошли незначительные изменения. Ниже приведена функция <code>main</code>, дополненная обращением к <code>setjmp</code>. Оно позволяет запомнить то нормальное состояние, которое будет использовано при восстановлении после ошибки. В функции <code>execerror</code> происходит соответствующее обращение к <code>longjmp</code>. (Описание <code>setjmp</code> и <code>longjmp</code> см. в разд. 7.5.)</p>
    <p><code>...</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <p><code>#include &lt;ctype.h&gt;</code></p>
    <p><code>char *progname;</code></p>
    <p><code>int lineno = 1;</code></p>
    <empty-line/>
    <p><code>#include &lt;signal.h&gt;</code></p>
    <p><code>#include &lt;setjmp.h&gt;</code></p>
    <p><code>jmp_buf begin;</code></p>
    <empty-line/>
    <p><code>main(argc, argv) /* hoc2 */</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> int fpecatch();</code></p>
    <p><code> progname = argv[0];</code></p>
    <p><code> setjmp(begin);</code></p>
    <p><code> signal(SIGFPE, fpecatch);</code></p>
    <p><code> yyparse();</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>execerror(s, t) /* recover from run-time error */</code></p>
    <p><code> char *s, *t;</code></p>
    <p><code>{</code></p>
    <p><code> warning(s, t);</code></p>
    <p><code> longjmp(begin, 0);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>fpecatch() /* catch floating point exceptions */</code></p>
    <p><code>{</code></p>
    <p><code> execerror("floating point exception", (char*)0);</code></p>
    <p><code>}</code></p>
    <p>В целях отладки мы сочли удобным, чтобы функция <code>execerror</code> вызывала <code>abort</code> (см. справочное руководство по <code>abort(3)</code>), что приведет к распечатке содержимого памяти, которую затем смогут использовать программы <code>adb</code> и <code>sdb</code>. Когда разработка программы полностью завершится, обращение к <code>abort</code> будет заменено на <code>longjmp</code>.</p>
    <p>В программе <code>hoc2</code> лексический анализатор несколько иной. В нем учтено различие строчных и прописных букв, а поскольку теперь <code>yyval</code> является объединением, нужно выбрать подходящий элемент перед выходом из <code>yylex</code>. Ниже показаны измененные фрагменты:</p>
    <p><code>yylex() /* hoc2 */</code></p>
    <p><code>{</code></p>
    <p><code> ...</code></p>
    <p><code> if (с == '.' || isdigit(c)) { /* number */</code></p>
    <p><code>  ungetc(c, stdin);</code></p>
    <p><code>  scanf("%lf", &amp;yylval.val);</code></p>
    <p><code>  return NUMBER;</code></p>
    <p><code> }</code></p>
    <p><code> if (islower(c)) {</code></p>
    <p><code>  yylval.index = с - 'a'; /* ASCII only */</code></p>
    <p><code>  return VAR;</code></p>
    <p><code> }</code></p>
    <p><code>...</code></p>
    <p>Еще раз отметим, что тип лексемы (т.е. <code>NUMBER</code>) не совпадает с ее значением (например, 3.1416).</p>
    <p>Продемонстрируем новые возможности <code>hoc2</code> переменные и способность восстановления после ошибки:</p>
    <p><code>$ hoc2</code></p>
    <p><code>x = 355</code></p>
    <p><code>355</code></p>
    <p><code>y = 113</code></p>
    <p><code>113</code></p>
    <p><code>p = x/z                            </code>z не определено, а значит, равно 0</p>
    <p><code>hoc2: division by zero near line 4 </code>Восстановление после ошибки</p>
    <p><code>x/y</code></p>
    <p><code>3.1415929</code></p>
    <p><code>1е30 * 1е30                        </code>Переполнение</p>
    <p><code>hoc2: floating point exception near line 5</code></p>
    <p><code>...</code></p>
    <p>В самом деле, для PDP-11 требуются вполне конкретные меры, чтобы обнаружить переполнение вещественного, но на большинстве других машин <code>hoc2</code> действует так, как показано выше.</p>
    <cite>
     <subtitle>Упражнение 8.3</subtitle>
     <p>Обеспечьте возможность запоминания последнего вычисленного значения, чтобы его не приходилось вводить снова для последовательности связанных вычислений. Одним из решений может быть использование какой-либо переменной, например <code>'p'</code>, в качестве "предыдущего" (previous) значения.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 8.4</subtitle>
     <p>Измените программу <code>hoc</code> так, чтобы можно было использовать символ <code>';'</code> как разделитель выражений наравне с символом перевода строки.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>8.3 Этап 3: переменные с произвольными именами; встроенные функции</p>
    </title>
    <p>В версию <code>hoc3</code> добавлено несколько новых средств, из-за чего увеличился текст программы. Основное нововведение возможность обращения к встроенным функциям:</p>
    <p><code>sin cos atan exp log log10 sqrt int abs</code></p>
    <p>Введена также дополнительно операция возведения в степень <code>'^'</code> (право ассоциативная с наивысшим приоритетом).</p>
    <p>Поскольку лексический анализатор должен справляться с встроенными именами длиной более чем в один символ, не так уж много усилий придется приложить, чтобы допустить переменные с именами произвольной длины. Для хранения информации об этих переменных нужна довольно сложная таблица имен, но если мы ее создаем, то можно заранее задать в ней вместе с именами значения некоторых полезных констант:</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>PI</code></td>
      <td align="left" valign="top">3.14159265358979323846</td>
      <td align="left" valign="top">Число &#960;</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>E</code></td>
      <td align="left" valign="top">2.71828182845904523536</td>
      <td align="left" valign="top">Основание натурального логарифма</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>GAMMA</code></td>
      <td align="left" valign="top">0.57721566490153286060</td>
      <td align="left" valign="top">Константа Эйлера-Маскерони</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>DEG</code></td>
      <td align="left" valign="top">57.2957795130823208768</td>
      <td align="left" valign="top">Отношение градуса к радиану</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>PHI</code></td>
      <td align="left" valign="top">1.61803398874989484820</td>
      <td align="left" valign="top">Золотое сечение</td>
     </tr>
    </table>
    <p>В результате получим полезный калькулятор:</p>
    <p><code>$ hoc3</code></p>
    <p><code>1.5^2.3</code></p>
    <p><code> 2.5410306</code></p>
    <p><code>exp(2*3*log(1.5))</code></p>
    <p><code> 2.5410306</code></p>
    <p><code>sin(PI/2)</code></p>
    <p><code> 1</code></p>
    <p><code>atan(1)*DEG</code></p>
    <p><code> 45</code></p>
    <p>Несколько улучшилась и работа распознавателя. В <code>hoc2</code> присваивание <code>x = expr</code> не только вызывало присваивание, но и приводило к печати значения, поскольку все выражения печатаются:</p>
    <p><code>$ hoc2</code></p>
    <p><code>x=2*3.14159</code></p>
    <p><code>6.28318 </code>В случае присваивания переменной значение печатается</p>
    <p>В программе <code>hoc3</code> проводится различие между присваиваниями и выражениями; значения печатаются только для выражений:</p>
    <p><code>$ hoc3</code></p>
    <p><code>x=2*3.14159 </code>Присваивание: значение не печатается</p>
    <p><code>x           </code>Выражение:</p>
    <p><code>6.28318     </code>Значение печатается</p>
    <p>Получившаяся в результате всех этих изменений программа настолько велика (около 250 строк текста), что для простоты редактирования и ускорения компиляции лучше разбить ее на отдельные файлы. Итак, теперь мы имеем пять файлов вместо одного:</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>hoc.y</code></td>
      <td align="left" valign="top">грамматика, <code>main</code>, <code>yylex</code> (как и прежде);</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>hoc.h</code></td>
      <td align="left" valign="top">глобальные структуры данных для включения в другие файлы;</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>symbol.c</code></td>
      <td align="left" valign="top">функции, работающие с таблицей имен: <code>lookup</code>, <code>install</code>;</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>unit.c</code></td>
      <td align="left" valign="top">встроенные функции и константы; <code>init</code>;</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>math.c</code></td>
      <td align="left" valign="top">функции для вызова стандартных математических функций: <code>Sqrt</code>, <code>Log</code> и т.д.</td>
     </tr>
    </table>
    <p>Необходимо более детально познакомиться с работой Си программы, состоящей из нескольких файлов, и программы <code>make</code>, чтобы переложить на нее часть своих обязанностей.</p>
    <p>Вернемся снова к программе <code>make</code> и рассмотрим вначале структуру таблицы имен. Поименованный объект имеет имя, тип (<code>VAR</code> или <code>BLTIN</code>) и значение. Так, объект типа <code>VAR</code> имеет значение <code>double</code>; если объект является встроенным, то его значением служит указатель на функцию, возвращающую <code>double</code>. Данная информация используется в <code>hoc.y</code>, <code>symbol.c</code> и <code>init.c</code>. Ее можно размножить в трех экземплярах, но тогда легко ошибиться или забыть исправить один из экземпляров при внесении изменений. Вместо этого мы поместили общую информацию в файл макроопределений <code>hoc.h</code>, который можно включить при необходимости в любой файл. (Окончание <code>.h</code> традиционно, но не контролируется никакими программами.) В файл <code>makefile</code> также добавлены сведения о зависимости исходных файлов от <code>hoc.h</code>, чтобы при изменении <code>hoc.h</code> была проведена требуемая перекомпиляция.</p>
    <p><code>$ cat hoc.h</code></p>
    <p><code>typedef struct Symbol { /* symbol table entry */</code></p>
    <p><code> char *name;</code></p>
    <p><code> short type; /* VAR, BLTIN, UNDEF */</code></p>
    <p><code> union {</code></p>
    <p><code>  double val; /* if VAR */</code></p>
    <p><code>  double (*ptr)(); /* if BLTIN */</code></p>
    <p><code> } u;</code></p>
    <p><code> struct Symbol *next; /* to link to another */</code></p>
    <p><code>} Symbol;</code></p>
    <p><code>Symbol *install(), *lookup();</code></p>
    <p><code>$</code></p>
    <p>Тип <code>UNDEF</code> обозначает <code>VAR</code>, которой пока не присвоили значения. Объекты связаны в список с помощью элемента <code>next</code> в записи <code>Symbol</code>. Сам список является локальным для <code>symbol.c</code>, доступ к нему возможен только посредством функций <code>lookup</code> и <code>install</code>. Это позволяет в случае необходимости легко менять структуру таблицы имен (что мы уже сделали однажды). Функция <code>lookup</code> отыскивает в списке заданное имя и возвращает указатель на <code>Symbol</code>, если имя найдено, и 0 в противном случае. Таблица имен рассчитана на линейный поиск, что вполне допустимо для диалогового калькулятора, поскольку поиск имен выполняется не во время его работы, а в процессе разбора. Функция <code>install</code> вносит переменную и связанные с ней тип и значение в начало списка. Функция <code>emalloc</code> обращается к стандартной функции размещения <code>malloc</code> (см. справочное руководство по <code>malloc(3)</code>) и проверяет результат. Указанные три функции составляют содержимое файла <code>symbol.c</code>. Файл <code>y.tab.h</code> создается при выполнении команды <code>yacc -d</code>; он содержит операторы <code>#define</code>, порождаемые <code>yacc</code> для лексем <code>NUMBER</code>, <code>VAR</code>, <code>BLTIN</code> и т.д.</p>
    <p><code>$ cat symbol.c</code></p>
    <p><code>#include "hoc.h"</code></p>
    <p><code>#include "y.tab.h"</code></p>
    <empty-line/>
    <p><code>static Symbol *symlist = 0; /* symbol table: linked list */</code></p>
    <empty-line/>
    <p><code>Symbol *lookup(s) /* find s in symbol table */</code></p>
    <p><code> char *s;</code></p>
    <p><code>{</code></p>
    <p><code> Symbol *sp;</code></p>
    <empty-line/>
    <p><code> for (sp = symlist; sp != (Symbol*)0; sp = sp-&gt;next)</code></p>
    <p><code>  if (strcmp(sp-&gt;name, s) == 0)</code></p>
    <p><code>   return sp;</code></p>
    <p><code> return 0; /* 0 ==&gt; not found */</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>Symbol *install(s, t, d) /* install s in symbol table */</code></p>
    <p><code> char *s;</code></p>
    <p><code> int t;</code></p>
    <p><code> double d;</code></p>
    <p><code>{</code></p>
    <p><code> Symbol *sp;</code></p>
    <p><code> char *emalloc();</code></p>
    <empty-line/>
    <p><code> sp = (Symbol*)emalloc(sizeof(Symbol));</code></p>
    <p><code> sp-&gt;name = emalloc(strlen(s)+1); /* +1 for '\0' */</code></p>
    <p><code> strcpy(sp-&gt;name, s);</code></p>
    <p><code> sp-&gt;type = t;</code></p>
    <p><code> sp-&gt;u.val = d;</code></p>
    <p><code> sp-&gt;next = symlist; /* put at front of list */</code></p>
    <p><code> symlist = sp;</code></p>
    <p><code> return sp;</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>char *emalloc(n) /* check return from malloc */</code></p>
    <p><code> unsigned n;</code></p>
    <p><code>{</code></p>
    <p><code> char *p, *malloc();</code></p>
    <empty-line/>
    <p><code> p = malloc(n);</code></p>
    <p><code> if (p == 0)</code></p>
    <p><code>  execerror("out of memory", (char*)0);</code></p>
    <p><code> return p;</code></p>
    <p><code>}</code></p>
    <p><code>$</code></p>
    <p>Файл <code>init.c</code> содержит определения констант (<code>PI</code> и т.п.) и указатели на встроенные функции; они заносятся в таблицу имен функцией <code>init</code>, находящейся в <code>main</code>.</p>
    <p><code>$ cat init.c</code></p>
    <p><code>#include "hoc.h"</code></p>
    <p><code>#include "y.tab.h"</code></p>
    <p><code>#include &lt;math.h&gt;</code></p>
    <empty-line/>
    <p><code>extern double Log(), Log10(), Exp(), Sqrt(), integer();</code></p>
    <empty-line/>
    <p><code>static struct { /* Constants */</code></p>
    <p><code> char *name;</code></p>
    <p><code> double cval;</code></p>
    <p><code>} consts[] = {</code></p>
    <p><code> "PI",   3.14159265358979323846,</code></p>
    <p><code> "E",     2.71828182845904523536,</code></p>
    <p><code> "GAMMA", 0.57721566490153286060, /* Euler */</code></p>
    <p><code> "DEG",  57.29577951308232087680, /* deg/radian */</code></p>
    <p><code> "PHI",   1.61803398874989484820, /* golden ratio */</code></p>
    <p><code> 0,       0</code></p>
    <p><code>};</code></p>
    <empty-line/>
    <p><code>static struct { /* Built-ins */</code></p>
    <p><code> char *name;</code></p>
    <p><code> double (*func)();</code></p>
    <p><code>} builtins[] = {</code></p>
    <p><code> "sin",   sin,</code></p>
    <p><code> "cos",   cos,</code></p>
    <p><code> "atan",  atan,</code></p>
    <p><code> "log",   Log, /* checks argument */</code></p>
    <p><code> "log10", Log10, /* checks argument */</code></p>
    <p><code> "exp",   Exp, /* checks argument */</code></p>
    <p><code> "sqrt",  Sqrt, /* checks argument */</code></p>
    <p><code> "int",   integer,</code></p>
    <p><code> "abs",   fabs,</code></p>
    <p><code> 0,       0</code></p>
    <p><code>};</code></p>
    <empty-line/>
    <p><code>init() /* install constants and built-ins in table */</code></p>
    <p><code>{</code></p>
    <p><code> int i;</code></p>
    <p><code> Symbol *s;</code></p>
    <empty-line/>
    <p><code> for (i = 0; consts[i].name; i++)</code></p>
    <p><code>  install(consts[i].name, VAR, consts[i].cval);</code></p>
    <p><code> for (i = 0; builtins[i].name; i++) {</code></p>
    <p><code>  s = install(builtins[i].name, BLTIN, 0.0);</code></p>
    <p><code>  s-&gt;u.ptr = builtins[i].func;</code></p>
    <p><code> }</code></p>
    <p><code>}</code></p>
    <p>Данные хранятся в таблицах, а не вводятся в текст программы, чтобы легче было их читать и изменять. Таблицы определены как статические, что обеспечивает их доступность только в данном файле. Мы вскоре вернемся к обсуждению стандартных математических функций типа <code>Log</code> и <code>Sqrt</code>.</p>
    <p>Построив такой базис, можно перейти к изменениям в грамматике, которые осуществляются на его основе.</p>
    <p><code>$ cat hoc.y</code></p>
    <p><code>%{</code></p>
    <p><code>#include "hoc.h"</code></p>
    <p><code>extern double Pow();</code></p>
    <p><code>%}</code></p>
    <p><code>%union {</code></p>
    <p><code> double val;  /* actual value */</code></p>
    <p><code> Symbol *sym; /* symbol table pointer */</code></p>
    <p><code>}</code></p>
    <p><code>%token &lt;val&gt; NUMBER</code></p>
    <p><code>%token &lt;sym&gt; VAR BLTIN UNDEF</code></p>
    <p><code>%type  &lt;val&gt; expr asgn</code></p>
    <p><code>%right '='</code></p>
    <p><code>%left  '+'</code></p>
    <p><code>%left  '*' '/'</code></p>
    <p><code>%left  UNARYMINUS</code></p>
    <p><code>%right '^' /* exponentiation */</code></p>
    <p><code>%%</code></p>
    <p><code>list: /* nothing */</code></p>
    <p><code> | list '\n'</code></p>
    <p><code> | list asgn '\n'</code></p>
    <p><code> | list expr '\n' { printf("\t%.8g\n", $2); }</code></p>
    <p><code> | list error '\n' { yyerrok; }</code></p>
    <p><code> ;</code></p>
    <p><code>asgn: VAR '=' expr { $$=$1-&gt;u.val=$3; $1-&gt;type = VAR; }</code></p>
    <p><code> ;</code></p>
    <p><code>expr: NUMBER</code></p>
    <p><code> | VAR {</code></p>
    <p><code>  if ($1-&gt;type == UNDEF)</code></p>
    <p><code>  execerror("undefined variable", $1-&gt;name);</code></p>
    <p><code>  $$ = $1-&gt;u.val;</code></p>
    <p><code> }</code></p>
    <p><code> | asgn</code></p>
    <p><code> | BLTIN '(' expr ')' { $$ = (*($1-&gt;u.ptr))($3); }</code></p>
    <p><code> | expr '+' expr { $$ = $1 + $3; }</code></p>
    <p><code> | expr '-' expr { $$ = $1 - $3; }</code></p>
    <p><code> | expr '*' expr { $$ = $1 * $3; }</code> </p>
    <p><code> | expr '/' expr {</code></p>
    <p><code>  if ($3 == 0.0)</code></p>
    <p><code>   execerror("division by zero", ""); $$ = $1 / $3;</code></p>
    <p><code>  }</code></p>
    <p><code> | expr '^' expr { $$ = Pow($1, $3); }</code></p>
    <p><code> | '(' expr ')' { $$ = $2; }</code></p>
    <p><code> | '-' expr %prec UNARYMINUS { $$ = -$2; }</code></p>
    <p><code> ;</code></p>
    <p><code>%%</code></p>
    <p><code>/* end of grammar */</code></p>
    <p><code>...</code></p>
    <p>Теперь в грамматике присутствует <code>asgn</code> для присваивания, подобно <code>expr</code> для выражения. Входная строка, состоящая только из</p>
    <p><code>VAR = expr</code></p>
    <p>является присваиванием, и, следовательно, ни одно из значений не печатается. Заметьте, кстати, как мы легко добавили к грамматике операцию возведения в степень, являющуюся правоассоциативной.</p>
    <p>Для стека <code>yacc</code> используется другое определение <code>%union</code>: вместо представления переменной как индекса в массиве из 26 элементов введен указатель на объект типа <code>Symbol</code>. Файл макроопределений <code>hoc.h</code> содержит определение этого типа.</p>
    <p>Лексический анализатор распознает имена переменных, находит их в таблице имен и определяет, относятся ли они к переменным (<code>VAR</code>) или к встроенным функциям (<code>BLTIN</code>). Функция <code>yylex</code> возвращает один из указанных типов. Заметим, что определенные пользователем переменные и предопределенные переменные типа <code>PI</code> относятся к <code>VAR</code>.</p>
    <p>Одно из свойств переменной состоит в том, что ей может быть присвоено либо не присвоено значение, поэтому обращение к не определенной переменной должно диагностироваться программой <code>yyparse</code> как ошибка. Возможность проверки переменной (определена она или нет) должна быть предусмотрена в грамматике, а не в лексическом анализаторе. Когда <code>VAR</code> распознается на лексическом уровне, контекст пока еще не известен, но нам не нужны сообщения о том, что <code>x</code> не определен, хотя контекст и вполне допустимый, как, например, <code>x</code> в присваивании типа <code>x = 1</code>.</p>
    <p>Ниже приводится измененная часть функции <code>yylex</code>:</p>
    <p><code>yylex() /* hoc3 */</code></p>
    <p><code>{</code></p>
    <p><code> ...</code></p>
    <p><code> if (isalpha(c)) {</code></p>
    <p><code>  Symbol *s;</code></p>
    <p><code>  char sbuf[100], *p = sbuf;</code></p>
    <p><code>  do {</code></p>
    <p><code>   *p++ = c;</code></p>
    <p><code>  } while ((c=getchar()) != EOF &amp;&amp; isalnum(c));</code></p>
    <p><code>  ungetc(c, stdin);</code></p>
    <p><code>  *p = '\0';</code></p>
    <p><code>  if ((s=lookup(sbuf)) == 0)</code></p>
    <p><code>   s = install(sbuf, UNDEF, 0.0);</code></p>
    <p><code>  yylval.sym = s;</code></p>
    <p><code>  return s-&gt;type == UNDEF ? VAR : s-&gt;type;</code></p>
    <p><code> }</code></p>
    <p><code> ...</code></p>
    <p>В функции <code>main</code> добавлена еще одна строка, в которой вызывается процедура инициации <code>init</code> для занесения в таблицу имен встроенных и предопределенных имен типа <code>PI</code>:</p>
    <p><code>main(argc, argv) /* hoc3 */</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> int fpecatch();</code></p>
    <empty-line/>
    <p><code> progname = argv[0];</code></p>
    <p><code> init();</code></p>
    <p><code> setjmp(begin);</code></p>
    <p><code> signal(SIGFPE, fpecatch);</code></p>
    <p><code> yyparse();</code></p>
    <p><code>}</code></p>
    <p>Теперь остался только файл <code>math.с</code>. Для некоторых стандартных математических функций требуется обработка ошибок для диагностики и восстановления, например, стандартная функция по умолчанию возвращает 0, если аргумент отрицателен. Функции из файла <code>math.с</code> используют контроль ошибок, описанный в разд. 2 справочного руководства по UNIX (см. гл. 7). Это более надежный и переносимый вариант, чем введение своих проверок, так как, вероятно, конкретные ограничения функций полнее учитываются в "официальной" программе. Файл макроопределений <code>&lt;math.h&gt;</code> содержит описания типов для стандартных математических функций, а файл <code>&lt;errno.h&gt;</code> — определения фатальных ошибок:</p>
    <p><code>$ cat math.с</code></p>
    <p><code>#include &lt;math.h&gt;</code></p>
    <p><code>#include &lt;errno.h&gt;</code></p>
    <empty-line/>
    <p><code>extern int errno;</code></p>
    <p><code>double errcheck();</code></p>
    <empty-line/>
    <p><code>double Log(x)</code></p>
    <p><code> double x;</code></p>
    <p><code>{</code></p>
    <p><code> return errcheck(log(x), "log");</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>double Log10(x)</code></p>
    <p><code> double x;</code></p>
    <p><code>{</code></p>
    <p><code> return errcheck(log10(x), "log10");</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>double Sqrt(x)</code></p>
    <p><code> double x;</code></p>
    <p><code>{</code></p>
    <p><code> return errcheck(sqrt(x), "sqrt");</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>double Exp(x)</code></p>
    <p><code> double x;</code></p>
    <p><code>{</code></p>
    <p><code> return errcheck(exp(x), "exp");</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>double Pow(x, y)</code></p>
    <p><code> double x, y;</code></p>
    <p><code>{</code></p>
    <p><code> return errcheck(pow(x,y), "exponentiation");</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>double integer(x)</code></p>
    <p><code> double x;</code></p>
    <p><code>{</code></p>
    <p><code> return (double)(long)x;</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>double errcheck(d, s) /* check result of library call */</code></p>
    <p><code> double d;</code></p>
    <p><code> char *s;</code></p>
    <p><code>{</code></p>
    <p><code> if (errno == EDOM) {</code></p>
    <p><code>  errno = 0;</code></p>
    <p><code>  execerror(s, "argument out of domain");</code></p>
    <p><code> } else if (errno == ERANGE) {</code></p>
    <p><code>  errno = 0;</code></p>
    <p><code>  execerror(s, "result out of range");</code></p>
    <p><code> }</code></p>
    <p><code> return d;</code></p>
    <p><code>}</code></p>
    <p>Любопытная, хотя грамматически неясная, диагностики появится при запуске yacc с новой грамматикой:</p>
    <p><code>$ yacc hoc.y</code></p>
    <p><code>conflicts: 1 shift/reduce</code></p>
    <p><code>$</code></p>
    <p>Сообщение <code>shift/reduce</code> означает, что грамматика <code>hoc3</code> неоднозначна: единственная входная строка</p>
    <p><code>x=1</code></p>
    <p>может быть разобрана двумя способами.</p>
    <image l:href="#img_5.jpeg"/>
    <p>Анализатор может решить, что <emphasis>присв</emphasis> сводится к <emphasis>выраж</emphasis>, а затем к <emphasis>список</emphasis>, как показано в левом дереве разбора, или что нужно применить заключающий символ <code>\n</code> сразу (shift — перенос) и преобразовать все в список, не используя промежуточных выводов, как в правом дереве разбора. Встретив неоднозначность, <code>yacc</code> выбирает перенос, так как это почти всегда правильное решение для реальных грамматик. Вы должны понимать такие сообщения, чтобы быть уверенным, что <code>yacc</code> сделал правильный выбор<a l:href="#n16" type="note">[16]</a>. Запуск <code>yacc</code> с флагом <code>-v</code> порождает обширный файл с именем <code>y.output</code>, который поможет вам найти причины конфликтов.</p>
    <cite>
     <subtitle>Упражнение 8.5</subtitle>
     <p>В данной версии <code>hoc3</code> допустимо присваивание:</p>
     <p><code>PI=3</code></p>
     <p>Хорошо ли это? Как бы вы изменили <code>hoc3</code>, чтобы запретить присваивание "констант"?</p>
    </cite>
    <cite>
     <subtitle>Упражнение: 8.6</subtitle>
     <p>Добавьте к грамматике встроенную функцию <code>atan2(x, y)</code> для вычисления величины угла, тангенс которого равен <code>x/y</code>. Добавьте встроенную функцию <code>rand()</code>, вырабатывающую случайные вещественные числа, равномерно распределенные на интервале [0,1). Как бы вам пришлось изменить грамматику, чтобы разрешить встроенные функции с разным числом аргументов?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 8.7</subtitle>
     <p>Как ввести дополнительное средство для выполнения команд прямо в <code>hoc</code>, подобно операции <code>!</code> в программах UNIX?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 8.8</subtitle>
     <p>Переработайте текст <code>math.c</code> так, чтобы можно было использовать таблицу, а не предложенное выше множество идентичных функций.</p>
    </cite>
    <subtitle>Еще одно замечание относительно <code>make</code></subtitle>
    <p>Поскольку теперь программа <code>hoc3</code> размещается не в одном, а в пяти файлах, <code>makefile</code> становится более сложным:</p>
    <p><code>$ cat makefile</code></p>
    <p><code>YFLAGS = -d # force creation of y.tab.h</code></p>
    <p><code>OBJS = hoc.o init.o math.o symbol.o # abbreviation</code></p>
    <empty-line/>
    <p><code>hoc3: $(OBJS)</code></p>
    <p><code>      cc $(OBJS) -lm -o hoc3</code></p>
    <empty-line/>
    <p><code>hoc.o: hoc.h</code></p>
    <empty-line/>
    <p><code>init.o symbol.o: hoc.h y.tab.h</code></p>
    <empty-line/>
    <p><code>pr:</code></p>
    <p><code>       @pr hoc.y hoc.h init.c math.c symbol.c makefile</code></p>
    <empty-line/>
    <p><code>clean:</code></p>
    <p><code>       rm -f $(OBJS) y.tab.[ch]</code></p>
    <p><code>$</code></p>
    <p>Строка <code>YFLAGS = -d</code> добавляет флаг <code>-d</code> в командную строку запуска yacc, создаваемую <code>make</code>. Этот флаг предписывает yacc создать файл <code>y.tab.h</code>, содержащий операторы <code>#define</code>. Строка <code>OBJS = ...</code> вводит сокращение для записи конструкции, используемой последовательно несколько раз. Синтаксис здесь не такой, как для переменных интерпретатора, скобки обязательны. Флаг <code>-lm</code> указывает, что математические функции нужно искать в библиотеке <code>libm.a</code>.</p>
    <p>Теперь программа <code>hoc3</code> образуется из четырех файлов <code>.о</code>, причем некоторые из них в свою очередь зависят от файлов <code>.h</code>. "Зная" эти зависимости, <code>make</code> может рассчитать, какая требуется перетрансляция в случае изменения любого из указанных файлов. Если вы хотите выяснить действия <code>make</code>, не запуская процесс, то попробуйте ввести команду</p>
    <p><code>$ make -n</code></p>
    <p>С другой стороны, если необходимо установить временную согласованность файлов, с помощью флага <code>-t</code> (touch исправить) вы можете как бы модифицировать файлы, не производя перетрансляции.</p>
    <p>Обратите внимание на то, что мы ввели не только множество зависимостей между исходными файлами, но и несколько полезных процедур, сконцентрировав их в одном файле. По умолчанию программа <code>make</code> выполняет первое действие, указанное в файле <code>makefile</code>. Однако если на первом месте окажется элемент, помечающий правило зависимости, такой, как <code>symbol.o</code> или <code>pr</code>, то выполняться будет он. Считается, что в случае "пустой" зависимости элемент всегда берется не из последней версии, поэтому при запросе он обязательно должен изменяться. Итак,</p>
    <p><code>$ make pr | lpr</code></p>
    <p>инициирует распечатку зависимостей файлов на принтере. (Появление символа <code>@</code> в <code>"@pr"</code> подавляет эхо выполняемой команды, запущенной с помощью <code>make</code>.) Команда же</p>
    <p><code>make clean</code></p>
    <p>удаляет выходные файлы <code>yacc</code>, а также файлы <code>.o</code>.</p>
    <p>Такой механизм пустых зависимостей в файле <code>makefile</code> часто оказывается более предпочтительным по сравнению с командным файлом как средство для концентрации в одном файле всех связанных операций. Область применения команды <code>make</code> не ограничивается только разработкой программ, она позволяет сгруппировать в единый набор все операции, имеющие временные зависимости.</p>
    <subtitle>Несколько замечаний относительно <code>lex</code></subtitle>
    <p>Программа <code>lex</code> порождает лексические анализаторы аналогично тому, как <code>yacc</code> генерирует программы грамматического разбора: вы создаете описание лексических правил вашего языка с помощью регулярных выражений и фрагментов Си программ, которые будут выполняться при обнаружении строки, соответствующей шаблону. Программа <code>lex</code> строит по этой информации распознаватель. Программы <code>lex</code> и <code>yacc</code> взаимодействуют таким же образом, как и описанные выше лексические анализаторы. Мы не собираемся здесь детально рассматривать <code>lex</code>; наша цель — заинтересовать вас, а подробности вы найдете в справочном руководстве по UNIX (том 2B).</p>
    <p>Вначале приведем <code>lex</code>-программу из файла <code>lex.l</code>, которая заменяет применявшуюся до сих пор функцию <code>yylex</code>:</p>
    <p><code>$ cat lex.l</code></p>
    <p><code>%{</code></p>
    <p><code>#include "hoc.h"</code></p>
    <p><code>#include "y.tab.h"</code></p>
    <empty-line/>
    <p><code>extern int lineno;</code></p>
    <p><code>%}</code></p>
    <p><code>%%</code></p>
    <p><code>[ \t] { ; } /* skip blanks and tabs */</code></p>
    <p><code>[0-9]+\.?][0-9]*\.[0-9]+ {</code></p>
    <p><code> sscanf(yytext, "%lf", &amp;yylval.val);</code></p>
    <p><code> return NUMBER;</code></p>
    <p><code>}</code></p>
    <p><code>[a-zA-Z][a-zA-Z0-9]* {</code></p>
    <p><code> Symbol *s;</code></p>
    <p><code> if ((s=lookup(yytext)) == 0)</code></p>
    <p><code>  s = install(yytext, UNDEF, 0.0);</code></p>
    <p><code> yylval.sym = s;</code></p>
    <p><code> return s-&gt;type == UNDEF ? VAR : s-&gt;type;</code></p>
    <p><code>}</code></p>
    <p><code>\n { lineno++; return '\n'; }</code></p>
    <p><code>/* everything else */</code></p>
    <p><code>. { return yytext[0]; }</code></p>
    <p><code>$</code></p>
    <p>Каждое "правило" является регулярным выражением, как и те, что использовались в <code>egrep</code> или <code>awk</code>, однако в отличие от них <code>lex</code> распознает комбинации в стиле Си типа <code>\t</code> и <code>\n</code>. Действие заключено в фигурные скобки. Правила проверяются по порядку, а конструкции с символами <code>*</code> и <code>+</code> задают сколь угодно длинную строку. Если правило применимо к текущей части входного потока, то выполняется действие. Совпавшая с правилом входная строка доступна в <code>lex</code>-программе под именем <code>yytext</code>. Чтобы работать в <code>lex</code>, нужно изменить файл <code>makefile</code>: Программа <code>make</code></p>
    <p><code>$ cat makefile</code></p>
    <p><code>YFLAGS = -d</code></p>
    <p><code>OBJS = hoc.o lex.o init.o math.o symbol.o</code></p>
    <empty-line/>
    <p><code>hoc3: $(OBJS)</code></p>
    <p><code>      cc $(OBJS) -lm -ll -o hoc3</code></p>
    <empty-line/>
    <p><code>hoc.o: hoc.h</code></p>
    <empty-line/>
    <p><code>lex.o init.o symbol.o: hoc.h y.tab.h</code></p>
    <empty-line/>
    <p><code>...</code></p>
    <p><code>$</code></p>
    <p>"знает", как получить из файла <code>.l</code> настоящий файл <code>.o</code>; все, что требуется от нас, дать ей сведения о зависимостях. (Нужно добавить библиотеку <code>lex -ll</code> к списку каталогов, в которых ведет поиск команда сс, поскольку распознаватель, создаваемый <code>lex</code>, нуждается в дополнительных функциях.) Эффект получается весьма ощутимым, причем совершенно автоматически:</p>
    <p><code>$ make</code></p>
    <p><code>yacc -d hoc.y</code></p>
    <empty-line/>
    <p><code> conflicts: 1 shift/reduce</code></p>
    <p><code>сс -с y.tab.c</code></p>
    <p><code>rm y.tab.c</code></p>
    <p><code>mv y.tab.o hoc.o</code></p>
    <p><code>lex lex.l</code></p>
    <p><code>сс -с lex.yy.c</code></p>
    <p><code>rm lex.yy.c</code></p>
    <p><code>mv lex.yy.o lex.o</code></p>
    <p><code>сс -c init.c</code></p>
    <p><code>сс -c math.c</code></p>
    <p><code>сс -c symbol.c</code></p>
    <p><code>cc hoc.o lex.o init.o math.o symbol.o -lm -ll -o hoc3</code></p>
    <p><code>$</code></p>
    <p>Если один файл изменится, достаточно единственной команды <code>make</code> для получения действующей версии:</p>
    <p><code>$ touch lex.l </code>Смена времени модификации файла lex.l</p>
    <p><code>$ make</code></p>
    <p><code>lex lex.l</code></p>
    <p><code>cc -с lex.yy.c</code></p>
    <p><code>rm lex.yy.c</code></p>
    <p><code>mv lex.yy.o lex.o</code></p>
    <p><code>cc hoc.o lex.o init.o math.o symbol.o -ll -lm -o hoc3</code></p>
    <p><code>$</code></p>
    <p>Некоторое время мы дебатировали о том, следует ли считать обсуждение программы <code>lex</code> отступлением от нашей темы и поэтому показать ее кратко, а затем перейти к другим вопросам или рассматривать ее как основное средство для лексического анализа, когда язык становится слишком сложным. У нас были аргументы "за" и "против". Затруднения в работе с <code>lex</code> (помимо того, что пользователь должен изучить еще один язык) связаны с тем, что замедляется выполнение программы, а распознаватели оказываются более объемными и медленными, чем эквивалентные версии на языке Си. К тому же возникают трудности с механизмом ввода в некоторых особых случаях, таких, как восстановление после ошибки, а также с вводом из файла. Ни одна из перечисленных проблем не является существенной для <code>hoc</code>. К сожалению, из-за ограниченного объема книги мы вынуждены вернуться в последующих лексических анализаторах к Си. Однако создание версии с <code>lex</code> будет для вас хорошей практикой.</p>
    <cite>
     <subtitle>Упражнение 8.9</subtitle>
     <p>Сравните размеры двух версий <code>hoc3</code>. <emphasis>Подсказка</emphasis>: обратитесь к справочному руководству по <code>size(1)</code>.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>8.4 Этап 4: компиляция на машину</p>
    </title>
    <p>Мы постепенно приближаемся к созданию <code>hoc5</code> — интерпретатора языка со структурами управления. Программа <code>hoc4</code> является промежуточным звеном: она имеет те же операции, что и <code>hoc3</code>, но реализуется на базе интерпретатора, как <code>hoc5</code>. Мы действительно написали такую программу <code>hoc4</code> и в результате получили две программы с одинаковыми возможностями, что ценно для отладки. По мере разбора входного потока <code>hoc4</code> порождает код, рассчитанный на простую машину, а не выдает сразу результат. При определении конца оператора будет выполнен код, порожденный для вычисления нужного результата (т.е. произойдет "интерпретация").</p>
    <p>Под простой машиной здесь подразумевается стековая машина: когда появляется операнд, он заносится в стек, точнее, создаются команды, заносящие операнд в стек). Большинство операций над операндами выполняется в вершине стека. Например, при обработке присваивания</p>
    <p><code>x=2*y</code></p>
    <p>создаются следующие команды:</p>
    <p><code>constpush </code>Записать в стек: константа … константа2</p>
    <p><code>2</code></p>
    <p><code>varpush   </code>Записать указатель на таблицу имен в стек</p>
    <p><code>y         </code>… для переменной у</p>
    <p><code>eval      </code>Вычислить: заменить указатель значением</p>
    <p><code>mul       </code>Перемножить два верхних элемента; результат заменяет их</p>
    <p><code>varpush   </code>Записать указатель на таблицу имен в стек</p>
    <p><code>x         </code>… для переменной x</p>
    <p><code>assign    </code>Записать значение в переменную, убрать указатель</p>
    <p><code>pop       </code>Убрать верхний элемент из стека</p>
    <p><code>STOP      </code>Конец последовательности команд</p>
    <p>Когда выполняются команды, выражение вычисляется и результат записывается в <code>x</code>, как и указано в примечаниях. Последняя команда <code>pop</code> удаляет из стека верхний элемент, поскольку он больше не нужен.</p>
    <p>Стековые машины обычно реализуются с помощью простых интерпретаторов, и наш интерпретатор тоже не является исключением: это просто массив, содержащий операции и операнды. Операции представляют собой машинные команды: каждая из них суть обращение к функции с параметрами, которые следуют за командой. Некоторые операнды могут уже находиться в стеке, как было показано в приведенном выше примере.</p>
    <p>Структура таблицы имен для <code>hoc4</code> совпадает с таковой для <code>hoc3</code>: инициация проводится в <code>init.c</code>, и математические функции, находящиеся в <code>math.c</code>, одни и те же. Грамматика <code>hoc4</code> идентична грамматике <code>hoc3</code>, но действия совершенно иные. Вообще, каждое действие порождает машинные команды и все необходимые для них аргументы. Например, в случае появления <code>VAR</code> в выражении создаются три команды: команда <code>varpush</code>, указатель на таблицу имен для переменной и команда <code>eval</code>, которая заменяет при вычислении указатель на таблицу имен соответствующим значением. Код для <code>'*'</code> содержит одну команду <code>mul</code>, поскольку операнды для нее уже находятся в стеке.</p>
    <p><code>$ cat hoc.y</code></p>
    <p><code>%{</code></p>
    <p><code>#include "hoc.h"</code></p>
    <p><code>#define code2(c1,c2) code(c1); code(c2)</code></p>
    <p><code>#define code3(c1,c2,c3) code(c1); code(c2); code(c3)</code></p>
    <p><code>%}</code></p>
    <p><code>%union {</code></p>
    <p><code> Symbol *sym; /* symbol table pointer */</code></p>
    <p><code> Inst *inst;  /* machine instruction */</code></p>
    <p><code>}</code></p>
    <p><code>%token &lt;sym&gt; NUMBER VAR BLTIN UNDEF</code></p>
    <p><code>%right '='</code></p>
    <p><code>%left '+'</code></p>
    <p><code>%left '*' '/'</code></p>
    <p><code>%left UNARYMINUS</code></p>
    <p><code>%right '^' /* exponentiation */</code></p>
    <p><code>%%</code></p>
    <p><code>list: /* nothing */ | list '\n'</code></p>
    <p><code> | list asgn '\n' { code2(pop, STOP); return 1; }</code></p>
    <p><code> | list expr '\n' { code2(print, STOP); return 1; }</code></p>
    <p><code> | list error '\n' { yyerrok; }</code></p>
    <p><code> ;</code></p>
    <p><code>asgn: VAR '=' expr { code3(varpush, (Inst)$1, assign); }</code></p>
    <p><code> ;</code></p>
    <p><code>expr: NUMBER { code2(constpush, (Inst)$1); }</code></p>
    <p><code> | VAR { code3(varpush, (Inst)$1, eval); }</code></p>
    <p><code> | asgn</code></p>
    <p><code> | BLTIN '(' expr ')' { code2(bltin, (Inst)$1-&gt;u.ptr); }</code></p>
    <p><code> | '(' expr ')'</code></p>
    <p><code> | expr '+' expr { code(add); }</code></p>
    <p><code> | expr '-' expr { code(sub); }</code></p>
    <p><code> | expr '*' expr { code(mul); }</code></p>
    <p><code> | expr '/' expr { code(div); }</code></p>
    <p><code> | expr '^' expr { code(power); }</code></p>
    <p><code> | '-' expr %prec UNARYMINUS { code (negate); }</code></p>
    <p><code> ;</code></p>
    <p><code>%%</code></p>
    <p><code>/* end of grammar */</code></p>
    <p><code>...</code></p>
    <p><code>Inst</code> является типом данных машинной команды (указатель на функцию, возвращающую <code>int</code>), к обсуждению которого мы вскоре вернемся. Обратите внимание на то, что аргументами для программы <code>code</code> служат имена функций, т.е. указатели на функции или другие совместимые с ними величины.</p>
    <p>Мы несколько изменили процедуру <code>main</code>. Теперь происходит возврат из анализатора после выполнения каждого оператора или выражения, и порожденный код выполняется. При обнаружении файла <code>yyparse</code> возвращает нуль.</p>
    <p><code>main(argc, argv) /* hoc4 */</code></p>
    <p><code> char *argv[];</code></p>
    <p><code>{</code></p>
    <p><code> int fpecatch();</code></p>
    <empty-line/>
    <p><code> progname = argv[0];</code></p>
    <p><code> init();</code></p>
    <p><code> setjmp(begin);</code></p>
    <p><code> signal(SIGFPE, fpecatch);</code></p>
    <p><code> for (initcode(); yyparse(); initcode())</code></p>
    <p><code>  execute(prog);</code></p>
    <p><code> return 0;</code></p>
    <p><code>}</code></p>
    <p>Лексический анализатор отличается мало в основном тем, что числа следует сохранять, а не использовать немедленно. Для этого достаточно занести их в таблицу имен вместе с переменными. Ниже приведена измененная часть <code>yylex</code>:</p>
    <p><code>yylex() /* hoc4 */</code></p>
    <p><code> ...</code></p>
    <p><code> if (с == '.' || isdigit(c)) {</code></p>
    <p><code>  /* number */</code></p>
    <p><code>  double d;</code></p>
    <p><code>  ungetc(c, stdin);</code></p>
    <p><code>  scanf("%lf", &amp;d);</code></p>
    <p><code>  yylval.sym = install("", NUMBER, d);</code></p>
    <p><code>  return NUMBER;</code></p>
    <p><code> }</code></p>
    <p><code> ...</code></p>
    <p>Каждый элемент стека интерпретатора является вещественным значением или указателем на запись в таблице имен; тип данных стека объединение всех элементов. Сама машина реализуется как массив указателей на процедуры, выполняющие операции типа <code>mul</code>, или на данные в таблице имен. Файл макроопределений <code>hoc.h</code> увеличивается, поскольку он должен включить эти структуры данных и описания функций для интерпретатора, чтобы они были доступны программе в целом. (Кстати, мы предпочли поместить всю информацию в один файл, а не в два, хотя для больших программ ее целесообразно разделить на несколько файлов с тем, чтобы включать каждый из них только там, где он действительно нужен.)</p>
    <p><code>$ cat hoc.h</code></p>
    <p><code>typedef struct Symbol { /* symbol table entry */</code></p>
    <p><code> char *name;</code></p>
    <p><code> short type; /* VAR, BLTIN, UNDEF */</code></p>
    <p><code> union {</code></p>
    <p><code>  double val; /* if VAR */</code></p>
    <p><code>  double (*ptr)(); /* if BLTIN */</code></p>
    <p><code> } u;</code></p>
    <p><code> struct Symbol *next; /* to link to another */</code></p>
    <p><code>} Symbol;</code></p>
    <p><code>Symbol *install(), *lookup();</code></p>
    <empty-line/>
    <p><code>typedef union Datum { /* interpreter stack type */</code></p>
    <p><code> double val;</code></p>
    <p><code> Symbol *sym;</code></p>
    <p><code>} Datum;</code></p>
    <p><code>extern Datum pop();</code></p>
    <empty-line/>
    <p><code>typedef int (*Inst)(); /* machine instruction */</code></p>
    <p><code>#define STOP (Inst) 0</code></p>
    <empty-line/>
    <p><code>extern Inst prog[];</code></p>
    <p><code>extern eval(), add(), sub(), mul(), div(), negate(), power();</code></p>
    <p><code>extern assign(), bltin(), varpush(), constpush(), print();</code></p>
    <p><code>$</code></p>
    <p>Процедуры, выполняющие машинные команды и управляющие стеком, хранятся в файле с именем <code>code.c</code>. Поскольку содержимое файла составляет около 150 строк, мы покажем его по частям:</p>
    <p><code>$ cat code.c</code></p>
    <p><code>#include "hoc.h"</code></p>
    <p><code>#include "y.tab.h"</code></p>
    <empty-line/>
    <p><code>#define NSTACK 256</code></p>
    <p><code>static Datum stack[NSTACK]; /* the stack */</code></p>
    <p><code>static Datum *stackp; /* next free spot on stack */</code></p>
    <empty-line/>
    <p><code>#define NPROG 2000</code></p>
    <p><code>Inst prog[NPROG]; /* the machine */</code></p>
    <p><code>Inst *progp; /* next free spot for code generation */</code></p>
    <p><code>Inst *pc; /* program counter during execution */</code></p>
    <empty-line/>
    <p><code>initcode() /* initialize for code generation */</code></p>
    <p><code>{</code></p>
    <p><code> stackp = stack;</code></p>
    <p><code> progp = prog;</code></p>
    <p><code>}</code></p>
    <p><code>...</code></p>
    <p>Управление стеком осуществляется путем обращений к двум процедурам <code>push</code> и <code>pop</code>:</p>
    <p><code>push(d) /* push d onto stack */</code></p>
    <p><code> Datum d;</code></p>
    <p><code>{</code></p>
    <p><code> if (stackp &gt;= &amp;stack[NSTACK])</code></p>
    <p><code>  execerror("stack overflow", (char*)0);</code></p>
    <p><code> *stackp++ = d;</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>Datum pop() /* pop and return top elem from stack */</code></p>
    <p><code>{</code></p>
    <p><code> if (stackp &lt;= stack)</code></p>
    <p><code>  execerror("stack underflow", (char*)0);</code></p>
    <p><code> return *--stackp;</code></p>
    <p><code>}</code></p>
    <p>Машинные команды создаются в процессе разбора при обращении к функции <code>code</code>, которая просто вносит команду на первое свободное место массива <code>prog</code>. Она возвращает адрес команды (который не используется в <code>hoc4</code>):</p>
    <p><code>Inst *code(f) /* install one instruction or operand */</code></p>
    <p><code> Inst f;</code></p>
    <p><code>{</code></p>
    <p><code> Inst *oprogp = progp;</code></p>
    <p><code> if (progp &gt;= &amp;prog[NPROG])</code></p>
    <p><code>  execerror("program too big", (char*)0);</code></p>
    <p><code> *progp++ = f;</code></p>
    <p><code> return oprogp;</code></p>
    <p><code>}</code></p>
    <p>Выполнение машинной команды фантастически тривиально, а как мала процедура, которая "выполняет" машинные команды, когда уже определены все программы!</p>
    <p><code>execute(p) /* run the machine */</code></p>
    <p><code> Inst *p;</code></p>
    <p><code>{</code></p>
    <p><code> for (pc = p; *pc != STOP; )</code></p>
    <p><code>  (*(*pc++))();</code></p>
    <p><code>}</code></p>
    <p>В цикле выполняется функция, указываемая командой, на которую в свою очередь указывает счетчик команд <code>pc</code>. Значение <code>pc</code> увеличивается, что делает возможным выбор очередной команды. Команда с кодом операции <code>STOP</code> завершает цикл. Некоторые команды, например <code>constpush</code> и <code>varpush</code>, сами увеличивают <code>pc</code>, чтобы "перескочить" через любые аргументы, следующие за командой.</p>
    <p><code>constpush() /* push constant onto stack */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d;</code></p>
    <p><code> d.val = ((Symbol*)*pc++)-&gt;u.val;</code></p>
    <p><code> push(d);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>varpush() /* push variable onto stack */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d;</code></p>
    <p><code> d.sym = (Symbol*)(*pc++);</code></p>
    <p><code> push(d);</code></p>
    <p><code>}</code></p>
    <p>Оставшаяся часть описания машины проста. Так, арифметические операции в основном те же, и создаются они редактированием одного образца. Ниже показана операция <code>add</code>:</p>
    <p><code>add() /* add top two elems on stack */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d1, d2;</code></p>
    <p><code> d2 = pop();</code></p>
    <p><code> d1 = pop();</code></p>
    <p><code> d1.val += d2.val;</code></p>
    <p><code> push(d1);</code></p>
    <p><code>}</code></p>
    <p>Другие процедуры также просты:</p>
    <p><code>eval() /* evaluate variable on stack */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d;</code></p>
    <p><code> d = pop();</code></p>
    <p><code> if (d.sym-&gt;type == UNDEF)</code></p>
    <p><code> execerror("undefined variable", d.sym-&gt;name);</code></p>
    <p><code> d.val = d.sym-&gt;u.val;</code></p>
    <p><code> push(d);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>assign() /* assign top value to next value */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d1, d2;</code></p>
    <p><code> d1 = pop();</code></p>
    <p><code> d2 = pop();</code></p>
    <p><code> if (d1.sym-&gt;type != VAR &amp;&amp; d1.sym-&gt;type != UNDEF)</code></p>
    <p><code> execerror("assignment to non-variable", d1.sym-&gt;name);</code></p>
    <p><code> d1.sym-&gt;u.val = d2.val;</code></p>
    <p><code> d1.sym-&gt;type = VAR;</code></p>
    <p><code> push(d2);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>print() /* pop top value from stack, print it */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d;</code></p>
    <p><code> d = pop();</code></p>
    <p><code> printf("\t%.8g\n", d.val);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>bltin() /* evaluate built-in on top of stack */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d;</code></p>
    <p><code> d = pop();</code></p>
    <p><code> d.val = (*(double (*)())(*pc++))(d.val);</code></p>
    <p><code> push(d);</code></p>
    <p><code>}</code></p>
    <p>Самый сложный момент здесь операция приведения в функции, которая требует, чтобы <code>*pc</code> рассматривался как указатель на функцию, возвращающую <code>double</code>, и эта функция выполняется с <code>d.val</code> в качестве аргумента.</p>
    <p>Диагностические сообщения от функций <code>eval</code> и <code>assign</code> никогда не появятся, если программа работает нормально. Мы оставили их на случай возникновения недоразумений из-за какой-нибудь ошибки программы. Потери за счет увеличения времени выполнения и размера кода даже не так важны, как обнаружение ошибки при внесении необдуманных изменений (что мы и наблюдали несколько раз).</p>
    <p>Использование языка Си дает возможность работать с указателем на функцию, что позволяет писать компактные и эффективные программы.</p>
    <p>Альтернативное решение состоит в том, чтобы сделать операторы константами и сгруппировать семантические функции в большой переключатель в функции <code>execute</code>. Попытайтесь реализовать его в качестве упражнения.</p>
    <subtitle>И снова о <code>make</code></subtitle>
    <p>По мере увеличения исходного текста программы <code>hoc</code> возрастает необходимость механически отслеживать изменения и взаимозависимости. Неоценимую помощь здесь может оказать команда <code>make</code>: она автоматизирует процесс, который иначе пришлось бы выполнять вручную (и иногда с ошибками) или создавать для этого специальный командный файл.</p>
    <p>Мы сделаем еще две модификации в файле <code>makefile</code>. Первая связана с тем, что хотя несколько файлов и зависят от констант, определенных в yacc программе файла <code>y.tab.h</code>, нет нужды их перетранслировать, если не изменились сами константы, а изменение в тексте Си программы из файла <code>hoc.y</code> не влияет на другие файлы. В новой версии <code>makefile</code> файлы <code>.o</code> зависят от нового файла <code>x.tab.h</code>, который изменяется только при замене содержимого файла <code>y.tab.h</code>. Вторая модификация основана на том, что правило для <code>pr</code> (печать исходных файлов) зависит лишь от самих исходных файлов, а именно, печатаются только измененные файлы.</p>
    <p>Первая модификация позволяет существенно экономить время в случае больших программ, когда грамматика постоянна, а семантические действия меняются (обычная ситуация). Второе изменение обеспечивает экономию бумаги.</p>
    <p>Приведем <code>makefile</code> для <code>hoc4</code>:</p>
    <p><code>YFLAGS = -d</code></p>
    <p><code>OBJS = hoc.o code.o init.o math.o symbol.o</code></p>
    <empty-line/>
    <p><code>hoc4: $(OBJS)</code></p>
    <p><code>      cc $(OBJS) -lm -o hoc4</code></p>
    <empty-line/>
    <p><code>hoc.o code.o init.o symbol.o: hoc.h</code></p>
    <empty-line/>
    <p><code>code.o init.o symbol.o: x.tab.h</code></p>
    <empty-line/>
    <p><code>x.tab.h: y.tab.h</code></p>
    <p><code>      -cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h</code></p>
    <empty-line/>
    <p><code>pr: hoc.y hoc.h code.c init.c math.c symbol.c</code></p>
    <p><code>      @pr $?</code></p>
    <p><code>      @touch pr</code></p>
    <empty-line/>
    <p><code>clean:</code></p>
    <p><code>      rm -f $(OBJS) [xy].tab.[ch]</code></p>
    <p>Символ <code>'-'</code> перед командой <code>cmp</code> дает указание <code>make</code> продолжать выполнение даже в случае неудачи <code>cmp</code>; это позволяет не останавливать работу и при несуществующем файле <code>x.tab.h</code> (флаг <code>-s</code> предписывает команде <code>cmp</code> не производить вывод, но установить код завершения). Комбинация <code>$?</code> раскрывается как список элементов из правила с устаревшей версией. К сожалению, форма записи в <code>makefile</code> слабо связана с обозначениями в интерпретаторе.</p>
    <p>Проиллюстрируем изложенное выше на примере (в предположении, что все файлы последней версии):</p>
    <p><code>$ touch hoc.y</code> Изменим время для файла hoc.y</p>
    <p><code>$ make</code></p>
    <p><code>yacc -d hoc.y</code></p>
    <p><code> conflicts: 1 shift/reduce</code></p>
    <p><code>сс -с y.tab.c</code></p>
    <p><code>rm y.tab.c</code></p>
    <p><code>mv y.tab.o hoc.o</code></p>
    <p><code>cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h</code></p>
    <p><code>cc hoc.o code.o init.o math.o symbol.o -lm -o hoc4</code></p>
    <p><code>$ make -n p</code>r Печать измененных файлов</p>
    <p><code>pr hoc.y</code></p>
    <p><code>touch pr</code></p>
    <p><code>$</code></p>
    <p>Отметим, что, кроме <code>hoc.y</code>, файлы не перетранслировались, поскольку файл <code>y.tab.h</code> остался тем же.</p>
    <cite>
     <subtitle>Упражнение 8.10</subtitle>
     <p>Сделайте размеры стека и массива <code>prog</code> динамическими, чтобы для <code>hoc4</code> всегда хватало объема памяти, если только ее можно получить, обращаясь к функции <code>malloc</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 8.11</subtitle>
     <p>Измените <code>hoc4</code> так, чтобы использовать в функции <code>execute</code> вместо вызова функций переключатель по виду операции <code>+</code>. Каково соотношение версий по размеру исходного текста и по времени выполнения? Как приблизительно их сопоставить по сложности развития и поддержания?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>8.5 Этап 5: структуры управления и операции отношений</p>
    </title>
    <p>Версия <code>hoc5</code> оправдывает все затраты, связанные с созданием интерпретатора. В нее допустимо включать операторы <code>if-else</code>  и <code>while</code>, аналогичные операторам языка Си, группировать операторы с помощью <code>{</code> и <code>}</code> и использовать оператор <code>print</code>. Она содержит полный набор операций отношений (<code>&gt;</code>, <code>&gt;=</code>, и т.д.), а также операций <code>AND</code>, <code>OR</code>, <code>&amp;&amp;</code> и <code>||</code>. (Две последние операции не гарантируют вычисления слева направо, хотя такой подход принят в Си; вычисляются оба условия, даже если в этом нет необходимости.)</p>
    <p>Грамматику <code>hoc5</code> дополняют лексемы, нетерминальные символы и правила для <code>if</code>, <code>while</code>, фигурных скобок и операций отношений. Поэтому она получилась несколько больше, но не намного сложнее предыдущих версий (возможно, за исключением правил для <code>if</code> и <code>while</code>):</p>
    <p><code>$ cat hoc.y</code></p>
    <p><code>%{</code></p>
    <p><code>#include "hoc.h"</code></p>
    <p><code>#define code2(c1,c2) code(c1); code(c2)</code></p>
    <p><code>#define code3(c1,c2,c3) code(c1); code(c2); code(c3)</code></p>
    <p><code>%}</code></p>
    <p><code>%union {</code></p>
    <p><code> Symbol *sym; /* symbol table pointer */</code></p>
    <p><code> Inst *inst; /* machine instruction */</code></p>
    <p><code>}</code></p>
    <p><code>%token &lt;sym&gt; NUMBER PRINT VAR BLTIN UNDEF WHILE IF ELSE</code></p>
    <p><code>%type &lt;inst&gt; stmt asgn expr stmtlist cond while if end</code></p>
    <p><code>%right '='</code></p>
    <p><code>%left OR</code></p>
    <p><code>%left AND</code></p>
    <p><code>%left GT GE LT LE EQ NE</code></p>
    <p><code>%left '+'</code></p>
    <p><code>%left '*' '/'</code></p>
    <p><code>%left UNARYMINUS NOT</code></p>
    <p><code>%right '^'</code></p>
    <p><code>%%</code></p>
    <p><code>list: /* nothing */</code></p>
    <p><code> | list '\n'</code></p>
    <p><code> | list asgn '\n' { code2(pop, STOP); return 1; }</code></p>
    <p><code> | list stmt '\n' { code(STOP); return 1; }</code></p>
    <p><code> | list expr '\n' { code2(print, STOP); return 1; }</code></p>
    <p><code> | list error '\n' { yyerrok; }</code></p>
    <p><code> ;</code></p>
    <p><code>asgn: VAR '=' expr { $$=$3; code3(varpush, (Inst)$1, assign); }</code></p>
    <p><code> ;</code></p>
    <p><code>stmt: expr { code(pop); }</code></p>
    <p><code> | PRINT expr { code(prexpr); $$ = $2; }</code></p>
    <p><code> | while cond stmt end {</code></p>
    <p><code>  ($1)[1] = (Inst)$3; /* body of loop */</code></p>
    <p><code>  ($1)[2] = (Inst)$4; } /* end, if cond fails */</code></p>
    <p><code> | if cond stmt end { /* else-less if */</code></p>
    <p><code>  ($1)[1] = (Inst)$3; /* thenpart */</code></p>
    <p><code>  ($1)[3] = (Inst)$4; } /* end, if cond fails */</code></p>
    <p><code> | if cond stmt end ELSE stmt end { /* if with else */</code></p>
    <p><code>  ($1)[1] = (Inst)$3; /* thenpart */</code></p>
    <p><code>  ($1)[2] = (Inst)$6; /* elsepart */</code></p>
    <p><code>  ($1)[3] = (Inst)$7; } /* end, if cond fails */</code></p>
    <p><code> | '{' stmtlist '}' { $$ = $2; }</code></p>
    <p><code> ;</code></p>
    <p><code>cond: '(' expr ')' { code(STOP); $$ = $2; }</code></p>
    <p><code> ;</code></p>
    <p><code>while: WHILE { $$ = code3(whilecode, STOP, STOP); }</code></p>
    <p><code> ;</code></p>
    <p><code>if: IF { $$=code(ifcode); code3(STOP, STOP, STOP); }</code></p>
    <p><code> ;</code></p>
    <p><code>end: /* nothing */ { code(STOP); $$ = progp; }</code></p>
    <p><code> ;</code></p>
    <p><code>stmtlist: /* nothing */ { $$ = progp; }</code></p>
    <p><code> | stmtlist '\n'</code></p>
    <p><code> | stmtlist stmt</code></p>
    <p><code> ;</code></p>
    <p><code>expr: NUMBER { $$ = code2(constpush, (Inst)$1); }</code></p>
    <p><code> | VAR { $$ = code3(varpush, (Inst)$1, eval); }</code></p>
    <p><code> | asgn</code></p>
    <p><code> | BLTIN '(' expr ')'</code></p>
    <p><code>  { $$ = $3; code2(bltin, (Inst)$1-&gt;u.ptr); }</code></p>
    <p><code> | '(' expr ')' { $$ = $2; }</code></p>
    <p><code> | expr '+' expr { code(add); }</code></p>
    <p><code> | expr '-' expr { code(sub); }</code></p>
    <p><code> | expr '*' expr { code(mul); }</code></p>
    <p><code> | expr '/' expr { code(div); }</code></p>
    <p><code> | expr '^' expr { code (power); }</code></p>
    <p><code> | '-' expr %prec UNARYMINUS { $$ = $2; code(negate); }</code></p>
    <p><code> | expr GT expr { code(gt); }</code></p>
    <p><code> | expr GE expr { code(ge); }</code></p>
    <p><code> | expr LT expr { code(lt); }</code></p>
    <p><code> | expr LE expr { code(le); }</code></p>
    <p><code> | expr EQ expr { code(eq); }</code></p>
    <p><code> | expr NE expr { code(ne); }</code></p>
    <p><code> | expr AND expr { code(and); }</code></p>
    <p><code> | expr OR expr { code(or); }</code></p>
    <p><code> | NOT expr { $$ = $2; code(not); }</code></p>
    <p><code>%%</code></p>
    <p><code>/* end of grammar */</code></p>
    <p>В грамматике есть пять случаев неоднозначности типа сдвиг/свертка, подобных упомянутой в грамматике для <code>hoc3</code>.</p>
    <p>Обратите внимание на то, что команды <code>STOP</code>, завершающие последовательность операторов, теперь порождаются в нескольких местах. Как и прежде, <code>progp</code> здесь представляет собой адрес очередной создаваемой команды. При выполнении все команды <code>STOP</code> служат для завершения цикла в функции <code>execute</code>. Правило для понятия все по сути является подпрограммой, вызываемой из нескольких мест, именно оно порождает команду <code>STOP</code> и возвращает адрес следующей за ней команды.</p>
    <p>Команды, создаваемые для операторов <code>if</code> и <code>while</code>, требуют особого рассмотрения. Когда встречается ключевое слово <code>while</code>, порождается операция <code>whilecode</code>, и адрес этой команды возвращается в качестве значения правила</p>
    <p><code>пока: WHILE</code></p>
    <p>Но в то же самое время резервируются два следующих машинных слова, которые будут определены ниже. Далее создаются команды для выражения, которое образует условие в операторе <code>while</code>. Значение, возвращаемое правилом <code>cond</code>, является адресом начала команд для условия. После распознавания всего оператора <code>while</code> в два зарезервированных слова, вводимых за командой <code>whilecode</code>, заносится адрес начала тела цикла и адрес оператора, следующего за циклом. (Команда по этому адресу будет создана позднее.)</p>
    <p><code>| пока усл опер все {</code></p>
    <p><code> ($1)[1] = (Inst) $3 /* тело цикла */</code></p>
    <p><code> ($1)[2] = (Inst) $4 /* все, если условие неверно */</code></p>
    <p><code>}</code></p>
    <p>Здесь <code>$1</code> обозначает адрес команды <code>whilecode</code>, таким образом, <code>($1)[1]</code> и <code>($1)[2]</code> обозначают два следующих слова.</p>
    <p>Рисунок, приведенный ниже, может прояснить ситуацию:</p>
    <image l:href="#img_6.jpeg"/>
    <p>С оператором <code>if</code> дело обстоит аналогично, но резервируются три слова: для частей <code>then</code> и <code>else</code>, а также для оператора, следующего за <code>if</code>. Мы вскоре рассмотрим этот случай.</p>
    <p>Лексический анализ теперь несколько удлиняется в основном из-за необходимости распознавания дополнительных операций:</p>
    <p><code>yylex() /* hoc5 */</code></p>
    <p><code>{</code></p>
    <p><code> ...</code></p>
    <p><code> switch (с) {</code></p>
    <p><code> case '&gt;': return follow('=', GE, GT);</code></p>
    <p><code> case '&lt;': return follow('=', LE, LT);</code></p>
    <p><code> case '=': return follow('=', EQ, '=');</code></p>
    <p><code> case '!': return follow('=', NE, NOT);</code></p>
    <p><code> case '|': return follow('|', OR, '|');</code></p>
    <p><code> case '&amp;': return follow('&amp;', AND, '&amp;');</code></p>
    <p><code> case '\n': lineno++; return '\n';</code></p>
    <p><code> default: return c;</code></p>
    <p><code> }</code></p>
    <p><code>}</code></p>
    <p>Функция <code>follow</code> "смотрит" на один символ вперед и возвращает символ назад во входной поток с помощью <code>ungetc</code>, если он оказался не тем, который требовался:</p>
    <p><code>follow(expect, ifyes, ifno) /* look ahead for &gt;=, etc. */</code></p>
    <p><code>{</code></p>
    <p><code> int с = getchar();</code></p>
    <p><code> if (c == expect)</code></p>
    <p><code>  return ifyes;</code></p>
    <p><code> ungetc(c, stdin);</code></p>
    <p><code>  return ifno;</code></p>
    <p><code>}</code></p>
    <p>В файле <code>hoc.h</code> стало больше описаний функций, например всех отношений, но в общем его структура такая же, как и в <code>hoc4</code>. Ниже приведено несколько последних строк грамматики:</p>
    <p><code>$ cat hoc.h</code></p>
    <p><code>...</code></p>
    <p><code>typedef int (*Inst)(); /* machine instruction */</code></p>
    <p><code>#define STOP (Inst)0</code></p>
    <p><code>extern Inst prog[], *progp, *code();</code></p>
    <p><code>extern eval(), add(), sub(), mul(), div(), negate(), power();</code></p>
    <p><code>extern assign(), bltin(), varpush(), constpush(), print();</code></p>
    <p><code>extern prexpr();</code></p>
    <p><code>extern gt(), lt(), eq(), ge(), le(), ne(), and(), or(), not();</code></p>
    <p><code>extern ifcode(), whilecode();</code></p>
    <p><code>$</code></p>
    <p>Большая часть файла <code>code.c</code> также не изменилась, хотя, очевидно, здесь появилось много новых процедур для обработки операций отношений. Типичным примером может служить функция <code>le</code> ("less than equal to" меньше или равно).</p>
    <p><code>le() {</code></p>
    <p><code> Datum d1, d2;</code></p>
    <p><code> d2 = pop();</code></p>
    <p><code> d1 = pop();</code></p>
    <p><code> d1.val = (double)(d1.val &lt;= d2.val);</code></p>
    <p><code> push(d1);</code></p>
    <p><code>}</code></p>
    <p>He совсем тривиальными являются функции <code>whilecode</code> и <code>ifcode</code>. Чтобы понять их, необходимо уяснить, что функция <code>execute</code> последовательно обрабатывает команды до тех пор, пока не будет найдена команда <code>STOP</code>, после чего происходит возврат из <code>execute</code>. Процесс разбора построен таким образом, что команда <code>STOP</code> завершает каждую последовательность команд, которую нужно обработать за одно обращение к <code>execute</code>. Тело цикла <code>while</code>, а также условие и фрагменты оператора <code>if</code> после <code>then</code> и <code>else</code> обрабатываются с помощью рекурсивных обращений к <code>execute</code>, возврат из которых по завершении обработки осуществляется в функцию <code>execute</code> на один уровень вложенности выше. Управление этими рекурсивными обращениями обеспечивается в <code>whilecode</code> и <code>ifcode</code>. Последние и предназначены для обработки соответствующих операторов.</p>
    <p><code>whilecode() {</code></p>
    <p><code> Datum d;</code></p>
    <p><code> Inst *savepc = pc; /* loop body */</code></p>
    <empty-line/>
    <p><code> execute(savepc+2); /* condition */</code></p>
    <p><code> d = pop();</code></p>
    <p><code> while (d.val) {</code></p>
    <p><code>  execute(*((Inst **)(savepc))); /* body */</code></p>
    <p><code>  execute(savepc+2);</code></p>
    <p><code>  d = pop();</code></p>
    <p><code> }</code></p>
    <p><code> pc = *((Inst **)(savepc+1)); /* next statement */</code></p>
    <p><code>}</code></p>
    <p>Как уже отмечалось ранее, после операции <code>whilecode</code> размещается указатель на тело цикла, затем указатель на следующий оператор, а за ним команды условия. Когда вызывается <code>whilecode</code>, значение указателя <code>pc</code> уже увеличено, так что он содержит указатель на тело цикла. Таким образом, <code>pc+1</code> настроен на следующий оператор, а <code>pc+2</code> на команды условия.</p>
    <p>Функция <code>ifcode</code> аналогична предыдущей: при входе в нее <code>pc</code> ссылается на фрагмент посте <code>then</code>, <code>pc+1</code> на фрагмент посте <code>else</code>, <code>pc+2</code> на следующий оператор, а <code>pc+3</code> на условие.</p>
    <p><code>ifcode()</code></p>
    <p><code>{</code></p>
    <p><code> Datum d;</code></p>
    <p><code> Inst *savepc = pc; /* then part */</code></p>
    <empty-line/>
    <p><code> execute(savepc+3); /* condition */</code></p>
    <p><code> d = pop();</code></p>
    <p><code> if (d.val)</code></p>
    <p><code>  execute(*((Inst **)(savepc)));</code></p>
    <p><code> else if (*((Inst **)(savepc+1))) /* else part? */</code></p>
    <p><code>  execute(*((Inst **)(savepc+1)));</code></p>
    <p><code> pc = *((Inst**)(savepc+2)); /* next stmt */</code></p>
    <p><code>}</code></p>
    <p>Программа в файле <code>init.c</code> также немного увеличится за счет введения в нее таблицы ключевых слов, хранимых в таблице имен вместе с остальной информацией:</p>
    <p><code>$ cat init.с</code></p>
    <p><code>...</code></p>
    <p><code>static struct { /* Keywords */</code></p>
    <p><code> char *name;</code></p>
    <p><code> int kval;</code></p>
    <p><code>} keywords [] = {</code></p>
    <p><code> "if",    IF,</code></p>
    <p><code> "else",  ELSE,</code></p>
    <p><code> "while", WHILE,</code></p>
    <p><code> "print", PRINT,</code></p>
    <p><code> 0, 0,</code></p>
    <p><code>};</code></p>
    <p><code>...</code></p>
    <p>Для занесения в таблицу имен ключевых слов нужно организовать еще один цикл в функции <code>init</code>:</p>
    <p><code>...</code></p>
    <p><code>for (i = 0; keywords[i].name; i++)</code></p>
    <p><code> install(keywords[i].name, keywords[i].kval, 0.0);</code></p>
    <p><code>...</code></p>
    <p>Изменения в функциях, управляющих таблицей имен, не требуются; в файле <code>code.c</code> есть функция <code>prexpr</code>, которая вызывается при выполнении оператора вида <code>print</code> <emphasis>выраж</emphasis>.</p>
    <p><code>prexpr() /* print numeric value */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d;</code></p>
    <p><code> d = pop();</code></p>
    <p><code> printf ('"%.8g\n", d.val);</code></p>
    <p><code>}</code></p>
    <p>Это не та функция печати, которая автоматически вызывается для вывода окончательного результата вычислений. Здесь выбирается число из стека и добавляется символ перевода строки к выходному потоку.</p>
    <p>Теперь <code>hoc5</code> представляет собой вполне полезный калькулятор, хотя для серьезного программирования необходимы дополнительные средства. В приведенных ниже упражнениях предлагаются возможные решения.</p>
    <cite>
     <subtitle>Упражнение 8.12</subtitle>
     <p>Добавьте для отладки к <code>hoc5</code> средство печати создаваемых машинных команд в понятной форме.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 8.13</subtitle>
     <p>Введите в свою программу операции присваивания из языка Си вида <code>+=</code>, <code>*=</code> и т.п., а также операции инкремента и декремента <code>++</code> и <code>--</code>. Измените операции <code>&amp;&amp;</code> и <code>||</code> так, чтобы обеспечить вычисление слева направо и условное вычисление, как в Си программах.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 8.14</subtitle>
     <p>Введите в <code>hoc5</code> оператор <code>for</code>, как в Си-программах. Добавьте операторы <code>break</code> и <code>continue</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 8.15</subtitle>
     <p>Как бы вы изменили грамматику или лексический анализатор <code>hoc5</code> (или и то, и другое), чтобы сделать программу более "терпимой" к использованию символов перевода строки? Каким образом можно ввести символ в качестве синонима символа перевода строки? Как ввести в язык примечания? Какой синтаксис, по вашему мнению, нужно использовать?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 8.16</subtitle>
     <p>Добавьте к <code>hoc5</code> средства обработки прерываний, чтобы некорректные вычисления можно было остановить без потери значений уже вычисленных переменных.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 8.17</subtitle>
     <p>Неудобно создавать программный файл, запускать его на выполнение, а затем редактировать с целью внесения небольших изменений. Как бы вы изменили <code>hoc5</code>, чтобы создать команду редактирования, которая автоматически вызывала бы редактор с уже считанной копией вашей <code>hoc</code>-программы? <emphasis>Подсказка</emphasis>: изучите текст функции.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>8.6 Этап 6: функции и процедуры; ввод-вывод</p>
    </title>
    <p>На последнем из описываемых здесь этапе развития программа значительно разрастается: в нее добавляются процедуры и функции, средства печати строк символов наряду с числами и чтения чисел из стандартного входного потока. Кроме того, в язык <code>hoc6</code> вводятся аргументы имен файлов, включая имя <code>"-"</code>, обозначающее стандартный входной поток. Все эти изменения увеличивают программу на 235 строк, доводя ее общий размер до 810 строк. В результате <code>hoc</code> преобразуется из калькулятора в интерпретатор языка программирования. Полностью программа приводится в приложении 3.</p>
    <p>В грамматике вызовы функции определяются как выражения, а вызовы процедур как операторы. И то, и другое детально поясняется в приложении 2, где дается еще несколько примеров. Так, определение и использование процедуры печати всех чисел Фибоначчи, меньших заданного параметра, происходят следующим образом:</p>
    <p><code>$ cat fib</code></p>
    <p><code>proc fib() {</code></p>
    <p><code> a = 0</code></p>
    <p><code> b = 1</code></p>
    <p><code> while (b &lt; $1) {</code></p>
    <p><code>  print b</code></p>
    <p><code>  с = b</code></p>
    <p><code>  b = a+b</code></p>
    <p><code>  a = с</code></p>
    <p><code> }</code></p>
    <p><code> print "\n"</code></p>
    <p><code>}</code></p>
    <p><code>$ hoc6 fib -</code></p>
    <p><code>fib(1000)</code></p>
    <p><code> 1 1 2 3 5 8 13 21 34.55 89 144 233 377 610 987</code></p>
    <p><code>...</code></p>
    <p>Здесь также показано использование файлов: имя файла <code>"-"</code> задает стандартный входной поток.</p>
    <p>Ниже приведена функция "факториал":</p>
    <p><code>$ cat fac</code></p>
    <p><code>func fac() {</code></p>
    <p><code> if ($1 &lt;= 0) return 1 else return $1 * fac($1-1)</code></p>
    <p><code>}</code></p>
    <p><code>$ hoc6 fac -</code></p>
    <p><code>fac(0)</code></p>
    <p><code> 1</code></p>
    <p><code>fac(7)</code></p>
    <p><code> 5040</code></p>
    <p><code>fac(10)</code></p>
    <p><code> 3628800</code></p>
    <p><code>...</code></p>
    <p>Внутри процедуры или функции к параметрам можно обращаться с помощью <code>$1</code> и т.д., как в командных файлах, но, кроме того, допустимо присваивание параметрам. Функции и процедуры рекурсивны, но в качестве локальных переменных можно использовать только параметры; остальные переменные являются глобальными, т.е. доступными во всей программе.</p>
    <p>В языке <code>hoc</code> функции и процедуры различаются, что дает возможность проверки, ценной для освобождения стека. (Ведь так легко забыть выполнить возврат или записать липшее выражение и получить несбалансированный стек!)</p>
    <p>Требуется значительное число изменений для преобразования грамматики при переходе от <code>hoc5</code> к <code>hoc6</code>, но все они локальные. Нужны новые лексемы и нетерминальные символы, а в описание <code>%union</code> необходимо ввести новый элемент для хранения числа аргументов:</p>
    <p><code>$cat hoc.y</code></p>
    <p><code>...</code></p>
    <p><code>%{</code></p>
    <p><code>#include "hoc.h"</code></p>
    <p><code>#define code2(c1,c2) code(c1); code(c2)</code></p>
    <p><code>#define code3(c1,c2,c3) code(c1); code(c2); code(c3)</code></p>
    <p><code>%}</code></p>
    <p><code>%union {</code></p>
    <p><code> Symbol *sym; /* symbol table pointer */</code></p>
    <p><code> Inst *inst; /* machine instruction */</code></p>
    <p><code> int narg; /* number of arguments */</code></p>
    <p><code>}</code></p>
    <p><code>%token &lt;sym&gt; NUMBER STRING PRINT VAR BLTIN UNDEF WHILE IF ELSE</code></p>
    <p><code>%token &lt;sym&gt; FUNCTION PROCEDURE RETURN FUNC PROC READ</code></p>
    <p><code>%token &lt;narg&gt; ARG</code></p>
    <p><code>%type &lt;inst&gt; expr stmt asgn prlist stmtlist</code></p>
    <p><code>%type &lt;inst&gt; cond while if begin end</code></p>
    <p><code>%type &lt;sym&gt; procname</code></p>
    <p><code>%type &lt;narg&gt; arglist</code></p>
    <p><code>%right '='</code></p>
    <p><code>%left OR</code></p>
    <p><code>%left AND</code></p>
    <p><code>%left GT GE LT LE EQ NE</code></p>
    <p><code>%left '+'</code></p>
    <p><code>%left '*' '/'</code></p>
    <p><code>%left UNARYMINUS NOT</code></p>
    <p><code>%right '^'</code></p>
    <p><code>%%</code></p>
    <p><code>list: /* nothing */</code></p>
    <p><code> | list '\n'</code></p>
    <p><code> | list defn '\n'</code></p>
    <p><code> | list asgn '\n' { code2(pop, STOP); return 1; }</code></p>
    <p><code> | list stmt '\n' { code(STOP); return 1; }</code></p>
    <p><code> | list expr '\n' { code2(print, STOP); return 1; }</code></p>
    <p><code> | list error '\n' { yyerrok; }</code></p>
    <p><code> ;</code></p>
    <p><code>asgn: VAR '=' expr { code3(varpush,(Inst)$1,assign); $$=$3; }</code></p>
    <p><code> | ARG '=' expr</code></p>
    <p><code>  { defnonly("$"); code2(argassign,(Inst)$1); $$=$3;}</code></p>
    <p><code> ;</code></p>
    <p><code>stmt: expr { code(pop); }</code></p>
    <p><code> | RETURN { defnonly("return"); code(procret); }</code></p>
    <p><code> | RETURN expr</code></p>
    <p><code>  { defnonly("return"); $$=$2; code(funcret); }</code></p>
    <p><code> | PROCEDURE begin '(' arglist ')'</code></p>
    <p><code>  { $$ = $2; code3(call, (Inst)$1, (Inst)$4); }</code></p>
    <p><code> | PRINT prlist { $$ = $2; }</code></p>
    <p><code> | while cond stmt end {</code></p>
    <p><code>  ($1)[1] = (Inst)$3; /* body of loop */</code></p>
    <p><code>  ($1)[2] = (Inst)$4;</code></p>
    <p><code> } /* end, if cond fails */</code></p>
    <p><code> | if cond stmt end { /* else-less if */</code></p>
    <p><code>  ($1)[1] = (Inst)$3; /* thenpart */</code></p>
    <p><code>  ($1)[3] = (Inst)$4;</code></p>
    <p><code> } /* end, if cond fails */</code></p>
    <p><code> | if cond stmt end ELSE stmt end { /* if with else */</code></p>
    <p><code>  ($1)[1] = (Inst)$3; /* thenpart */</code></p>
    <p><code>  ($1)[2] = (Inst)$6; /* elsepart */</code></p>
    <p><code>  ($1)[3] = (Inst)$7;</code></p>
    <p><code> } /* end, if cond fails */</code></p>
    <p><code> | '{' stmtlist '}' { $$ = $2; }</code></p>
    <p><code> ;</code></p>
    <p><code>cond: '(' expr ')' { code(STOP); $$ = $2; }</code></p>
    <p><code> ;</code></p>
    <p><code>while: WHILE { $$ = code3(whilecode,STOP,STOP); }</code></p>
    <p><code> ;</code></p>
    <p><code>if: IF { $$ = code(ifcode); code3(STOP, STOP, STOP); }</code></p>
    <p><code> ;</code></p>
    <p><code>begin: /* nothing */ { $$ = progp; }</code></p>
    <p><code> ;</code></p>
    <p><code>end: /* nothing */ { code(STOP); $$ = progp; }</code></p>
    <p><code> ;</code></p>
    <p><code>stmtlist: /* nothing */ { $$ = progp; }</code></p>
    <p><code> | stmtlist '\n'</code></p>
    <p><code> | stmtlist stmt</code></p>
    <p><code> ;</code></p>
    <p><code>expr: NUMBER { $$ = code2(constpush, (Inst)$1); }</code></p>
    <p><code> | VAR { $$ = code3(varpush, (Inst)$1, eval); }</code></p>
    <p><code> | ARG { defnonly("$"); $$ = code2(arg, (Inst)$1); }</code></p>
    <p><code> | asgn</code></p>
    <p><code> | FUNCTION begin '(' arglist ')'</code></p>
    <p><code>  { $$ = $2; code3(call,(Inst)$1,(Inst)$4); }</code></p>
    <p><code> | READ '(' VAR ')' { $$ = code2(varread, (Inst)$3); }</code></p>
    <p><code> | BLTIN '(' expr ')' { $$=$3; code2(bltin, (Inst)$1-&gt;u.ptr); }</code></p>
    <p><code> | '(' expr ')' { $$ = $2; }</code></p>
    <p><code> | expr '+' expr { code(add); }</code></p>
    <p><code> | expr '-' expr { code(sub); }</code></p>
    <p><code> | expr '*' expr { code(mul); }</code></p>
    <p><code> | expr '/' expr { code(div); }</code></p>
    <p><code> | expr '^' expr { code (power); }</code></p>
    <p><code> | '-' expr %prec UNARYMINUS { $$=$2; code(negate); }</code></p>
    <p><code> | expr GT expr { code(gt); }</code></p>
    <p><code> | expr GE expr { code(ge); }</code></p>
    <p><code> | expr LT expr { code(lt); }</code></p>
    <p><code> | expr LE expr { code(le); }</code></p>
    <p><code> | expr EQ expr { code(eq); }</code></p>
    <p><code> | expr NE expr { code(ne); }</code></p>
    <p><code> | expr AND expr { code(and); }</code></p>
    <p><code> | expr OR expr { code(or); }</code></p>
    <p><code> | NOT expr { $$ = $2; code(not); }</code></p>
    <p><code> ;</code></p>
    <p><code>prlist: expr { code(prexpr); }</code></p>
    <p><code> | STRING { $$ = code2(prstr, (Inst)$1); }</code></p>
    <p><code> | prlist ',' expr { code(prexpr); }</code></p>
    <p><code> | prlist ',' STRING { code2(prstr, (Inst)$3); }</code></p>
    <p><code> ;</code></p>
    <p><code>defn: FUNC procname { $2-&gt;type=FUNCTION; indef=1; }</code></p>
    <p><code>  '(' ')' stmt { code(procret); define($2); indef=0; }</code></p>
    <p><code> | PROC procname { $2-&gt;type=PROCEDURE; indef=1; }</code></p>
    <p><code>  '(' ')' stmt { code(procret); define($2); indef=0; }</code></p>
    <p><code> ;</code></p>
    <p><code>procname: VAR</code></p>
    <p><code> | FUNCTION</code></p>
    <p><code> | PROCEDURE</code></p>
    <p><code> ;</code></p>
    <p><code>arglist: /* nothing */ { $$ = 0; }</code></p>
    <p><code> | expr { $$ = 1; }</code></p>
    <p><code> | arglist expr { $$ = $1 + 1; }</code></p>
    <p><code> ;</code></p>
    <p><code>%%</code></p>
    <p><code>/* end of grammar */</code></p>
    <p><code>...</code></p>
    <p>С помощью правила для <emphasis>аргсписок</emphasis> (список аргументов) подсчитывается число аргументов. На первый взгляд может показаться, что нужно каким-то образом собирать аргументы, но это не так, поскольку каждое выражение (<emphasis>выраж</emphasis>) из списка аргументов вырабатывает значение в стеке как раз там, где оно необходимо.</p>
    <p>Правило для <emphasis>опред</emphasis> вводит новое свойство языка <code>yacc</code>: встроенное действие. Оказывается, можно поместить действие посредине правила, так, чтобы оно выполнялось в процессе распознавания последнего. Мы воспользовались этой возможностью, чтобы запомнить, что сейчас распознается: определение функции или процедуры. (В качестве альтернативного решения можно было бы ввести новый символ типа <code>begin</code>, который распознавался бы в соответствующее время.) Функция <code>defnonly</code> печатает предупреждающее сообщение, если вопреки синтаксису какая-либо конструкция окажется вне определения функции или процедуры. Обычно вам предоставляется выбор: обнаруживать ошибку синтаксически или семантически. Перед нами уже стояла такая задача ранее, при диагностике неопределенных переменных. Функция <code>defnonly</code> хорошо иллюстрирует ситуацию, когда семантическая проверка легче синтаксической.</p>
    <p><code>defnonly(s) /* warn if illegal definition */</code></p>
    <p><code> char *s;</code></p>
    <p><code>{</code></p>
    <p><code> if (!indef)</code></p>
    <p><code>  execerror(s, "used outside definition");</code></p>
    <p><code>}</code></p>
    <p>Переменная <code>indef</code> определена в <code>hoc.y</code> и принимает значения в действиях для <emphasis>опред</emphasis>.</p>
    <p>К лексическому анализатору добавлены средства проверки аргументов: символ <code>$</code>, за которым следует чисто для строки в кавычках. Последовательности в строках, начинающиеся с обратной дробной черты, например <code>\n</code>, обрабатываются функцией <code>backslash</code>:</p>
    <p><code>yylex() /* hoc6 */</code></p>
    <p><code> ...</code></p>
    <p><code> if (c == '$') { /* argument? */</code></p>
    <p><code>  int n = 0;</code></p>
    <p><code>  while (isdigit(c=getc(fin)))</code></p>
    <p><code>   n = 10 * n + c — '0';</code></p>
    <p><code>  ungetc(с, fin);</code></p>
    <p><code>  if (n == 0)</code></p>
    <p><code>   execerror("strange $...", (char*)0);</code></p>
    <p><code>  yylval.narg = n;</code></p>
    <p><code>  return ARG;</code></p>
    <p><code> }</code></p>
    <p><code> if (c == '"') { /* quoted string */</code></p>
    <p><code>  char sbuf [100], *p, *emalloc();</code></p>
    <p><code>  for (p = sbuf; (c=getc(fin)) != '"'; p++) {</code></p>
    <p><code>   if (с == '\n' || c == EOF)</code></p>
    <p><code>    execerror("missing quote", "");</code></p>
    <p><code>   if (p &gt;= sbuf + sizeof (sbuf) - 1) {</code></p>
    <p><code>    *p = '\0';</code></p>
    <p><code>    execerror("string too long", sbuf);</code></p>
    <p><code>   }</code></p>
    <p><code>   *p = backslash(c);</code></p>
    <p><code>  }</code></p>
    <p><code>  *p = 0;</code></p>
    <p><code>  yylval.sym = (Symbol*)emalloc(strlen(sbuf)+1);</code></p>
    <p><code>  strcpy(yylval.sym, sbuf);</code></p>
    <p><code>  return STRING;</code></p>
    <p><code> }</code></p>
    <p><code> ...</code></p>
    <empty-line/>
    <p><code>backslash(c) /* get next char with \'s interpreted */</code></p>
    <p><code> int c;</code></p>
    <p><code>{</code></p>
    <p><code> char *index(); /* 'strchr()' in some systems */</code></p>
    <p><code> static char transtab[] = "b\bf\fn\nr\rt\t";</code></p>
    <p><code> if (c != '\\')</code></p>
    <p><code>  return c;</code></p>
    <p><code> c = getc(fin);</code></p>
    <p><code> if (islower(c) &amp;&amp; index(transtab, c))</code></p>
    <p><code>  return index(transtab, с)[1];</code></p>
    <p><code> return c;</code></p>
    <p><code>}</code></p>
    <p>Лексический анализатор является примером <emphasis>конечного автомата</emphasis> независимо от того, написан ли он на Си или получен с помощью порождающей программы типа <code>lex</code>. Наша первоначальная версия Си программы стала весьма сложной, и поэтому для всех программ, больших ее по объему, лучше использовать <code>lex</code>, чтобы максимально упростить внесение изменений.</p>
    <p>Остальные изменения сосредоточены главным образом в файле <code>code.c</code>, хотя несколько имен функций добавляется к файлу <code>hoc.h</code>. Машина остается той же, но с дополнительным стеком для хранения последовательности вложенных вызовов функций и процедур (проще ввести второй стек, чем загружать больший объем информации в существующий). Начало файла <code>code.c</code> выглядит так:</p>
    <p><code>$ cat code.c</code></p>
    <p><code>#include "hoc.h"</code></p>
    <p><code>#include "y.tab.h"</code></p>
    <p><code>#include &lt;stdio.h&gt;</code></p>
    <empty-line/>
    <p><code>#define NSTACK 256</code></p>
    <p><code>static Datum stack[NSTACK]; /* the stack */</code></p>
    <p><code>static Datum *stackp; /* next free spot on stack */</code></p>
    <empty-line/>
    <p><code>#define NPROG 2000</code></p>
    <p><code>Inst prog[NPROG]; /* the machine */</code></p>
    <p><code>Inst *progp; /* next free spot for code generation */</code></p>
    <p><code>Inst *pc; /* program counter during execution */</code></p>
    <p><code>Inst *progbase = prog; /* start of current subprogram */</code></p>
    <p><code>int returning; /* 1 if return stmt seen */</code></p>
    <empty-line/>
    <p><code>typedef struct Frame { /* proc/func call stack frame */</code></p>
    <p><code> Symbol *sp;  /* symbol table entry */</code></p>
    <p><code> Inst *retpc; /* where to resume after return */</code></p>
    <p><code> Datum *argn; /* n-th argument on stack */</code></p>
    <p><code> int nargs;   /* number of arguments */</code></p>
    <p><code>} Frame;</code></p>
    <empty-line/>
    <p><code>#define NFRAME 100 Frame frame[NFRAME];</code></p>
    <p><code>Frame *fp; /* frame pointer */</code></p>
    <empty-line/>
    <p><code>initcode() {</code></p>
    <p><code> progp = progbase;</code></p>
    <p><code> stackp = stack;</code></p>
    <p><code> fp = frame;</code></p>
    <p><code> returning = 0;</code></p>
    <p><code>}</code></p>
    <p><code>...</code></p>
    <p><code>$</code></p>
    <p>Поскольку теперь в таблице имен хранятся указатели на функции и процедуры, а также на строки для печати, необходимо расширить определение типа объединения в файле <code>hoc.h</code>:</p>
    <p><code>$ cat hoc.h</code></p>
    <p><code>typedef struct Symbol { /* symbol table entry */</code></p>
    <p><code> char *name;</code></p>
    <p><code> short type;</code></p>
    <p><code> union {</code></p>
    <p><code>  double val;      /* VAR */</code></p>
    <p><code>  double (*ptr)(); /* BLTIN */</code></p>
    <p><code>  int (*defn)();   /* FUNCTION, PROCEDURE */</code></p>
    <p><code>  char *str;       /* STRING */</code></p>
    <p><code> } u;</code></p>
    <p><code> struct Symbol *next; /* to link to another */</code></p>
    <p><code>} Symbol;</code></p>
    <p><code>$</code></p>
    <p>В процессе трансляции функция <code>define</code> заносит запись о функции в таблицу имен, сохраняет указатель на нее и изменяет в случае успешной компиляции адрес следующего после созданных команд свободного слова:</p>
    <p><code>define(sp) /* put func/proc in symbol table */</code></p>
    <p><code> Symbol *sp;</code></p>
    <p><code>{</code></p>
    <p><code> sp-&gt;u.defn = (Inst)progbase; /* start of code */</code></p>
    <p><code> progbase = progp; /* next code starts here */</code></p>
    <p><code>}</code></p>
    <p>Когда в процессе выполнения вызывается функция или процедура, все аргументы уже вычислены и помещены в стек (первый аргумент находится на наибольшем уровне). Код операции вызова (<code>call</code>) сопровождается указателем на таблицу имен и числом аргументов. Сохраняется образ стека, в котором содержится вся существенная информация о программе: запись в таблице имен, место возврата после вызова, место хранения аргументов в стеке выражений, а также число аргументов, сопровождающих вызов. Образ стека создается функцией <code>call</code>, которая затем выполняет тело программы.</p>
    <p><code>call() /* call a function */</code></p>
    <p><code>{</code></p>
    <p><code> Symbol *sp = (Symbol*)pc[0]; /* symbol table entry */</code></p>
    <p><code> /* for function */</code></p>
    <p><code> if (fp++ &gt;= &amp;frame[NFRAME-1])</code></p>
    <p><code>  execerror(sp-&gt;name, "call nested too deeply");</code></p>
    <p><code> fp-&gt;sp = sp;</code></p>
    <p><code> fp-&gt;nargs = (int)pc[1];</code></p>
    <p><code> fp-&gt;retpc = pc + 2;</code></p>
    <p><code> fp-&gt;argn = stackp - 1; /* last argument */</code></p>
    <p><code> execute(sp-&gt;u.defn);</code></p>
    <p><code> returning = 0;</code></p>
    <p><code>}</code></p>
    <p>Создаваемая структура показана на рис. 8.2.</p>
    <image l:href="#img_7.jpeg"/>
    <p><emphasis>Рис. 8.2</emphasis>: Структуры данных для вызова процедуры</p>
    <empty-line/>
    <p>В конце концов произойдет возврат из вызываемой программы при выполнении <code>procret</code> или <code>funcret</code>:</p>
    <p><code>funcret() /* return from a function */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d;</code></p>
    <p><code> if (fp-&gt;sp-&gt;type == PROCEDURE)</code></p>
    <p><code>  execerror(fp-&gt;sp-&gt;name, "(proc) returns value");</code></p>
    <p><code> d = pop(); /* preserve function return value */</code></p>
    <p><code> ret();</code></p>
    <p><code> push(d);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>procret() /* return from a procedure */</code></p>
    <p><code>{</code></p>
    <p><code> if (fp-&gt;sp-&gt;type == FUNCTION)</code></p>
    <p><code>  execerror(fp-&gt;sp-&gt;name(func) returns no value");</code></p>
    <p><code> ret();</code></p>
    <p><code>}</code></p>
    <p>Функция <code>ret</code> удаляет аргументы из стека, сохраняет указатель на образ стека <code>fp</code> и устанавливает счетчик команд:</p>
    <p><code>ret() /* common return from func or proc */</code></p>
    <p><code>{</code></p>
    <p><code> int i;</code></p>
    <p><code> for (i = 0; i &lt; fp-&gt;nargs; i++)</code></p>
    <p><code>  pop(); /* pop arguments */</code></p>
    <p><code> pc = (Inst*)fp-&gt;retpc;</code></p>
    <p><code> --fp;</code></p>
    <p><code> returning = 1;</code></p>
    <p><code>}</code></p>
    <p>Некоторые программы интерпретатора нуждаются в небольших поправках для учета ситуаций, когда происходит возврат во вложенных операторах. Решение не элегантно, но верно и состоит во введении признака с именем <code>returning</code>, который принимает значение 1 при обнаружении оператора <code>return</code>. Выполнение, организуемое функциями <code>ifcode</code>, <code>whilecode</code>, <code>execute</code>, завершается раньше, если установлен признак <code>returning</code>; в функции <code>call</code> он обнуляется.</p>
    <p><code>ifcode() {</code></p>
    <p><code> Datum d;</code></p>
    <p><code> Inst *savepc = pc; /* then part */</code></p>
    <p><code> execute(savepc+3); /* condition */</code></p>
    <p><code> d = pop();</code></p>
    <p><code> if (d.val)</code></p>
    <p><code>  execute(*((Inst**)(savepc)));</code></p>
    <p><code> else if (*((Inst**)(savepc+1))) /* else part? */</code></p>
    <p><code>  execute(*((Inst**)(savepc+1)));</code></p>
    <p><code> if (!returning)</code></p>
    <p><code>  pc = *((Inst**)(savepc+2)); /* next stmt */</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>whilecode() {</code></p>
    <p><code> Datum d;</code></p>
    <p><code> Inst *savepc = pc;</code></p>
    <p><code> execute(savepc+2); /* condition */</code></p>
    <p><code> d = pop();</code></p>
    <p><code> while (d.val) {</code></p>
    <p><code>  execute(*((Inst**)(savepc))); /* body */</code></p>
    <p><code>  if (returning)</code></p>
    <p><code>   break;</code></p>
    <p><code>  execute(savepc+2); /* condition */</code></p>
    <p><code>  d = pop();</code></p>
    <p><code> }</code></p>
    <p><code> if (!returning)</code></p>
    <p><code>  pc = *((Inst**)(savepc+1)); /* next stmt */</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>execute(p)</code></p>
    <p><code> Inst *p;</code></p>
    <p><code>{</code></p>
    <p><code> for (pc = p; *pc != STOP &amp;&amp; !returning; )</code></p>
    <p><code>  (*((++pc)[-1]))();</code></p>
    <p><code>}</code></p>
    <p>Аргументы выбираются для получения значения или присваивания с помощью функции <code>getarg</code>, которая следит за сбалансированностью стека:</p>
    <p><code>double *getarg() /* return pointer to argument */</code></p>
    <p><code>{</code></p>
    <p><code> int nargs = (int)*pc++;</code></p>
    <p><code> if (nargs &gt; fp-&gt;nargs)</code></p>
    <p><code>  execerror(fp-&gt;sp-&gt;name, "not enough arguments");</code></p>
    <p><code> return &amp;fp-&gt;argn[nargs - fp-&gt;nargs].val;</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>arg() /* push argument onto stack */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d;</code></p>
    <p><code> d.val = *getarg();</code></p>
    <p><code> push(d);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>argassign() /* store top of stack in argument */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d;</code></p>
    <p><code> d = pop();</code></p>
    <p><code> push(d); /* leave value on stack */</code></p>
    <p><code> *getarg() = d.val;</code></p>
    <p><code>}</code></p>
    <p>Функции <code>prstr</code> и <code>prexpr</code> печатают строки и числа:</p>
    <p><code>prstr() /* print string value */</code></p>
    <p><code>{</code></p>
    <p><code> printf("%s", (char*)*pc++);</code></p>
    <p><code>}</code></p>
    <empty-line/>
    <p><code>prexpr() /* print numeric value */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d;</code></p>
    <p><code> d = pop();</code></p>
    <p><code> printf("%.8g d.val);</code></p>
    <p><code>}</code></p>
    <p>Функция <code>varread</code> читает переменные. Она возвращает 0 при обнаружении конца файла и 1 — в противном случае, а также устанавливает значение указанной переменной:</p>
    <p><code>varread() /* read into variable */</code></p>
    <p><code>{</code></p>
    <p><code> Datum d;</code></p>
    <p><code> extern FILE *fin;</code></p>
    <p><code> Symbol *var = (Symbol*)*pc++;</code></p>
    <p><code>Again:</code></p>
    <p><code> switch (fscanf(fin, "%lf", &amp;var-&gt;u.val)) {</code></p>
    <p><code> case EOF:</code></p>
    <p><code>  if (moreinput())</code></p>
    <p><code>   goto Again;</code></p>
    <p><code>  d.val = var-&gt;u.val = 0.0;</code></p>
    <p><code>  break;</code></p>
    <p><code> case 0:</code></p>
    <p><code>  execerror("non-number read into", var-&gt;name);</code></p>
    <p><code>  break;</code></p>
    <p><code> default:</code></p>
    <p><code>  d.val = 1.0;</code></p>
    <p><code>  break;</code></p>
    <p><code> }</code></p>
    <p><code> var-&gt;type = VAR;</code></p>
    <p><code> push(d);</code></p>
    <p><code>}</code></p>
    <p>Обнаружив конец файла для текущего входного потока, функция <code>varread</code> обратится к <code>moreinput</code>, которая откроет следующий файл, заданный в качестве аргумента (если он есть). В функции <code>moreinput</code> обработка входной информации имеет некоторые нюансы, здесь не рассматриваемые; речь о них идет в приложении 3.</p>
    <p>Итак, мы завершили разработку программы <code>hoc</code>. Для сравнения приведем число непустых строк в каждой версии:</p>
    <p><code>hoc1</code> 59</p>
    <p><code>hoc2</code> 94</p>
    <p><code>hoc3</code> 248 (для версии с <code>lex</code> 229)</p>
    <p><code>hoc4</code> 396</p>
    <p><code>hoc5</code> 574</p>
    <p><code>hoc6</code> 809</p>
    <p>Конечно, эти значения были вычислены программным способом: $</p>
    <p><code>sed '/$/d' `pick *.[chyl]` | wc -l</code></p>
    <p>Безусловно, развитие языка может быть продолжено, и вам предоставляется такая возможность в приведенных ниже упражнениях.</p>
    <cite>
     <subtitle>Упражнение 8.18</subtitle>
     <p>Измените <code>hoc6</code> так, чтобы можно было использовать поименованные формальные параметры в подпрограммах вместо <code>$1</code> и т.д.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 8.19</subtitle>
     <p>Сейчас все переменные глобальны, за исключением параметров. Уже есть большая часть механизма для введения локальных переменных, хранимых в стеке. Одно из решений заключается во введении описания <code>auto</code>, которое резервирует место в стеке для перечисленных переменных; не перечисленные переменные считаются глобальными. Кроме того, придется расширить таблицу имен так, чтобы поиск в ней осуществлялся вначале для локальных, а затем для глобальных переменных. Как это связано с поименованными аргументами?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 8.20</subtitle>
     <p>Как бы вы ввели массивы в язык <code>hoc</code>? Как следует передавать их функциям и процедурам? Как возвращать их?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 8.21</subtitle>
     <p>Обобщите работу со строками так, чтобы переменные могли хранить строки, а не только числа. Какие операции потребуются для этого? Самая трудная часть управление памятью добейтесь динамичного хранения строк: память должна освобождаться, когда строки перестают быть нужными. В качестве промежуточного шага добавьте более развитые форматы печати, например, обеспечьте возможность использования некоторых форм стандартной Си функции <code>printf</code>.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>8.7 Оценка времени выполнения</p>
    </title>
    <p>Мы сравнивали <code>hoc</code> с другими программами-калькуляторами UNIX, чтобы приблизительно оценить, насколько хорошо он работает. К таблице, представленной ниже (табл. 8.1), можно, конечно, отнестись скептически, но она показывает "разумность" нашей реализации. Все приведенные в ней величины даны в секундах. Работа велась на PDP-11/70. Было выполнено два теста. Первый, вычисление функции Аккерманна <code>ack(3,3)</code>, — хороший тест для отработки механизма вызова функций. Здесь происходят 2432 вызова, причем некоторые из них достаточно глубоко вложены.</p>
    <p><code>func ack() {</code></p>
    <p><code> if ($1 == 0) return ($2+1)</code></p>
    <p><code> if($2 == 0) return (ack($1 - 1, 1))</code></p>
    <p><code> return (ack($1 - 1, ack($1, $2 - 1)))</code></p>
    <p><code>}</code></p>
    <p><code>ack(3,3)</code></p>
    <p>Второй тест — стократное вычисление чисел Фибоначчи со значениями, меньшими 1000. В этом случае выполнялись в основном арифметические операции с периодическим вызовом функций:</p>
    <p><code>proc fib() {</code></p>
    <p><code> a = 0</code></p>
    <p><code> b = 1</code></p>
    <p><code> while (b &lt; $1) {</code></p>
    <p><code>  с = b</code></p>
    <p><code>  b = a+b</code></p>
    <p><code>  a = c</code></p>
    <p><code> }</code></p>
    <p><code>}</code></p>
    <p><code>i = 1</code></p>
    <p><code>while (i &lt; 100) {</code></p>
    <p><code> fib(1000)</code></p>
    <p><code> i = i + 1</code></p>
    <p><code>}</code></p>
    <p>Тест выполнялся на четырех языках: <code>hoc</code>, <code>bc(1)</code>, <code>bas</code> (древний диалект Бейсика, который существует только на PDP-11) и Си (использовался тип PDP-11 для всех переменных) .</p>
    <p>Числа, приведенные в табл. 8.1, являются суммой пользовательского и системного времени процессора и вычислены с помощью функции <code>time</code>.</p>
    <table>
     <tr align="left">
      <th align="left" valign="top">Программа</th>
      <th align="left" valign="top">(3,3)</th>
      <th align="left" valign="top">100*fib(1000)</th>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>hoc</code></td>
      <td align="left" valign="top">5.5</td>
      <td align="left" valign="top">5.0</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>bas</code></td>
      <td align="left" valign="top">1.3</td>
      <td align="left" valign="top">0.7</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>bc</code></td>
      <td align="left" valign="top">39.7</td>
      <td align="left" valign="top">14.9</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>c</code></td>
      <td align="left" valign="top">&lt;0.1</td>
      <td align="left" valign="top">0.1</td>
     </tr>
    </table>
    <p>Таблица 8.1: <strong>Время работы на PDP-11/70 (в секундах)</strong></p>
    <empty-line/>
    <p>Можно также приспособить Си программу для определения количества времени, используемого каждой функцией. Программу нужно перетранслировать в режиме профилирования, введя флаг <code>-p</code> в каждой единице трансляции Си и при режиме загрузки. Если изменить файл <code>makefile</code> для чтения:</p>
    <p><code>hoc6: $(OBJS)</code></p>
    <p><code>      сс $(CFLAGS) $(OBJS) -lm -о hoc6</code></p>
    <p>чтобы команда <code>сс</code> задействовала переменную <code>CFLAGS</code>, а затем задать</p>
    <p><code>$ make clean; make CFLAGS=-p</code></p>
    <p>то результирующая программа будет выполняться с профилированием. После выполнения программы остается файл <code>mon.out</code>, который интерпретируется программой профилировщиком <code>prof</code>.</p>
    <p>Для иллюстрации изложенного мы протестировали <code>hoc6</code> на приведенной выше программе Фибоначчи:</p>
    <p><code>$ hoc6 &lt;fibtest       </code>Запуск теста</p>
    <p><code>$ prof hoc6 | sed 15q </code>Анализ</p>
    <p><code>name   %time cumsec #call ms/call</code></p>
    <p><code>_pop    15.6 0.85   32182  0.03</code></p>
    <p><code>_push   14.3 1.63   32182  0.02</code></p>
    <p><code>mcount  11.3 2.25</code></p>
    <p><code>csv     10.1 2.80</code></p>
    <p><code>cret     8.8 3.28</code></p>
    <p><code>_assign  8.2 3.73    5050  0.09</code></p>
    <p><code>_eval    8.2 4.18    8218  0.05</code></p>
    <p><code>_execute 6.0 4.51    3567  0.09</code></p>
    <p><code>_varpush 5.9 4.83   13268  0.02</code></p>
    <p><code>_lt      2.7 4.98    1783  0.08</code></p>
    <p><code>_constpu 2.0 5.09     497  0.22</code></p>
    <p><code>_add     1.7 5.18    1683  0.05</code></p>
    <p><code>_getarg  1.5 5.26    1683  0.05</code></p>
    <p><code>_yyparse 0.6 5.30       3 11.11</code></p>
    <p><code>$</code></p>
    <p>Результаты, полученные с помощью профилировщика, также подвержены случайным вариациям, как и те, что получены с помощью функции <code>time</code>, поэтому их следует считать лишь указанием настоящих значений, а не принимать за абсолютную истину. Тем не менее при необходимости приведенные значения могут помочь повысить быстродействие программы <code>hoc</code>. Приблизительно третья часть времени тратится на запись и чтение из стека. Накладные расходы еще более возрастут, если мы будем учитывать время выполнения функций связи <code>csv</code> и <code>cret</code> между программами Си (функция <code>mcount</code> представляет собой часть программы с профилированием, полученную с помощью команды ее <code>-р</code>.). Замена вызовов функций на макрообращения даст заметную разницу во времени выполнения.</p>
    <p>Для проверки этого предположения мы изменили <code>code.c</code>, заменив вызовы <code>push</code> и <code>pop</code> на макрокоманды, управляющие стеком:</p>
    <p><code>#define push(d) *stackp++ = (d)</code></p>
    <p><code>#define popm() *--stackp = pop() /* функция все-таки нужна */</code></p>
    <p>(Функция <code>pop</code> все-таки нужна в качестве кода операции нашей машины, поэтому нельзя заменить все обращения к ней.) Новая версия выполняется на 35% быстрее; время в табл. 8.1 сокращается от 5.5 до 3.7 с и от 5.0 до 3.1 с.</p>
    <cite>
     <subtitle>Упражнение 8.22</subtitle>
     <p>В макрокомандах <code>push</code> и <code>popm</code> не предусмотрен контроль ошибок. Прокомментируйте разумность такого решения. Как бы вы обеспечили контроль ошибок, производимый в версии с функциями, не снижая быстродействия макрокоманд?</p>
    </cite>
   </section>
   <section>
    <title>
     <p>8.8 Заключение</p>
    </title>
    <p>Ознакомившись с материалом этой главы, мы можем сделать важные выводы. Во-первых, средства для развития языков очень нужны, так как позволяют сконцентрировать внимание на интересной работе проектировании языка (с ним легко экспериментировать). Грамматика является организующей структурой при реализации: программы привязываются к грамматике и вызываются в подходящий момент в процессе разбора.</p>
    <p>Во-вторых, и это уже философский аспект, ценна сама постановка задачи речь идет о разработке языка, а не просто о написании программы. Построение программы как языкового процессора обеспечивает регулярность синтаксиса (т.е. взаимодействие с пользователем), делает более структурированной реализацию. Кроме того, мы получаем гарантию, что новые средства будут хорошо согласовываться с уже реализованными. Под "языками", конечно, следует понимать не только традиционные языки программирования, но и уже упоминавшиеся выше в примерах языки <code>eqn</code> и <code>pick</code>, а также <code>yacc</code>, <code>lex</code>, и <code>make</code>.</p>
    <p>Рассмотрены здесь и вопросы использования программных средств. В частности, показана роль программы <code>make</code>, которая предотвращает целый класс ошибок (например, вы забыли перетранслировать какую-то подпрограмму). Она позволяет избавиться от лишней работы и предоставляет удобный способ сгруппировать в одном файле большое число связанных и, возможно, зависимых операций.</p>
    <p>С помощью файлов макроопределений вы можете координировать описания данных, доступных более чем в одном файле. Проводя централизацию информации, они исключают ошибки, вызванные несогласованностью применяемых версий, особенно если действуют совместно с программой <code>make</code>. Весьма важно разбить данные и подпрограммы на файлы таким образом, чтобы они не были видимы, если в этом нет необходимости.</p>
    <p>Хотелось бы отметить, что из-за ограниченного объема книги мы мало внимания уделили тем средствам UNIX, которые применяются при разработке семейства программ <code>hoc</code>. Каждая версия программы находится в отдельном каталоге, для идентичных файлов используются связи; постоянно вызываются команды <code>ls</code> и <code>du</code>, чтобы следить за тем, какие файлы где находятся. На многие вопросы ответы дают сами программы. Например, на вопрос: "Где описана данная переменная?" отвечает программа <code>grep</code>. "Как мы внесли изменения в данную версию?" отвечает <code>idiff</code>. "Насколько велик файл?" отвечает <code>wc</code>. Пора делать копию файла обратитесь к команде <code>cp</code>. Нужно скопировать только те файлы, которые изменились со времени последнего копирования? Вам поможет в этом деле программа <code>make</code>.</p>
    <p>Такой общий подход типичен для повседневной разработки программ в системе UNIX: множество небольших программных средств, каждое в отдельности или их различные сочетания, позволяет автоматизировать работу, которую иначе пришлось бы выполнять вручную.</p>
    <cite>
     <subtitle>Историческая и библиографическая справка</subtitle>
     <p>Программа <code>yacc</code> создана С. Джонсоном. Класс языков, для которых yacc может создавать программу разбора, называется LALR-(1): разбор здесь ведется слева направо и входной поток просматривается не более чем на одну лексему вперед. Понятие раздельных описаний для задания приоритетов и разрешения неоднозначностей в грамматике появилось вместе с <code>yacc</code>. Этот вопрос рассматривается в статье А. В. Ахо, С. К. Джонсона и Д. Д. Ульмана "Deterministic parsing of ambiguous grammars" (CACM, August, 1975). Там же приведены новые алгоритмы и структуры данных для создания и хранения таблиц разбора.</p>
     <p>Основы теории, на базе которой построены <code>yacc</code> и другие программы анализаторы, излагаются в книге А. В. Ахо и Д. Д. Ульмана "Principles of Compiler Design" (Addison Wesley, 1977). Сама программа <code>yacc</code> описана в справочном руководстве по UNIX (том 2B). В этом разделе также представлен калькулятор, сравнимый с <code>hoc2</code>: для вас такое сравнение может оказаться полезным.</p>
     <p>Программа <code>lex</code> первоначально было написана М. Леском. Теория lex освещена в книге Ахо и Ульмана, а сам язык <code>lex</code> описан в справочном руководстве по UNIX.</p>
     <p>Программы <code>yacc</code> и в меньшей степени <code>lex</code> использовались для реализации многих языковых процессоров, включая переносимый компилятор Си, процессоры на Паскале, Фортране 77, Ратфоре, <code>awk</code>, <code>be</code>, <code>egn</code> и <code>pick</code>.</p>
     <p>Программа <code>make</code> создана С. Фельдманом и описана в статье "MAKE a program for maintaining computer programs" (Software-Practice &amp; Experience, April, 1979).</p>
     <p>В книге Д. Бентли "Writing Efficient Programs" (Prentice-Hall, 1982) обсуждается техника ускорения выполнения программ. Акцент в ней делается на создание подходящего алгоритма, а также на улучшение кода, если в этом есть необходимость.</p>
    </cite>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 9</p>
    <p>Подготовка документации</p>
   </title>
   <section>
    <p>Средства редактирования и форматирования документации стали одним из ранних приложений системы UNIX. Администрация Bell Labs, приобретая первую установку PDP-11, хотела иметь систему подготовки документации, а не операционную систему. (К счастью, заказчики получили больше, чем ожидали.)</p>
    <p>Первая форматирующая программа <code>roff</code> — небольшая, быстрая программа, с которой легко было работать до тех пор, пока выпускались простые документы с помощью печатающего устройства. Следующая форматирующая программа <code>nroff</code> обладала большими возможностями. Ее автор Дж. Осанна вместо того, чтобы обеспечивать каждый вид документации по желанию пользователя, сделал свою программу программируемой, т.е. многие задачи форматирования решались путем программирования на языке <code>nroff</code>.</p>
    <p>После приобретения в 1973 г. небольшого наборного устройства <code>nroff</code> была расширена и смогла поддерживать множество символов, а также шрифты разных кеглей и гарнитур. Новая программа получила название <code>troff</code> (по аналогии с "en-roff', а произносили это как "tee-roff".) Программы <code>nroff</code> и troff имели одну и ту же основу и один и тот же входной язык; <code>nroff</code> игнорировала команды типа смены размера шрифта, которые она не могла выполнить. В настоящей главе речь пойдет главным образом о <code>troff</code>, но большинство наших пояснений можно отнести и к <code>nroff</code> с учетом ограничений, налагаемых выводными устройствами.</p>
    <p>Самое большое достоинство <code>troff</code> гибкость базового языка и ее возможности программирования. Она может выполнить почти любую задачу форматирования. Но гибкость обходится недешево, использовать <code>troff</code> часто бывает весьма трудно. Следует отметить, что почти все программное обеспечение UNIX для подготовки документации сконструировано таким образом, чтобы заместить некоторые фрагменты "чистой <code>troff</code>". Одним из примеров может служить разметка страницы: общий вид документа, как выглядят названия, заголовки и абзацы, где появляются номера страниц, как велики размеры страницы и т.д. Все соответствующие средства не встроены и должны программироваться. Однако пользователю не нужно специфицировать перечисленные выше детали для каждого документа, так как есть стандартный пакет форматирующих команд. Пользователь пакета не говорит: "Следующая строка должна центрироваться, состоять из прописных букв, выделенных жирным шрифтом". Вместо этого он должен сказать: "Следующая строка — заголовок", и тогда используется определение вида названия из пакета. Пользователи имеют дело с логическими компонентами документа названиями, заголовками, абзацами, сносками и т.п., а не с размерами, шрифтами и позициями.</p>
    <p>К сожалению, планируемый как "стандартный" пакет команд форматирования не является более стандартным: распространение получили несколько пакетов плюс многие их варианты. Мы рассмотрим здесь два пакета общего назначения: <code>ms</code>, первоначальный "стандарт", и <code>mm</code>, более новую версию, стандартную в System V, а также пакет <code>man</code> для печати справочников. Основной акцент сделан на ms как на стандарт в седьмой версии; он может служить хорошим примером и является достаточно мощным для выполнения работы (с его помощью подготовлена наша книга, правда, пришлось добавить команду для задания требуемого нам шрифта).</p>
    <p>Этот опыт типичен — пакеты макроопределений подходят для выполнения многих задач форматирования, но иногда необходимо вернуться к основным командам <code>troff</code>. В данной главе мы опишем лишь небольшую часть <code>troff</code>.</p>
    <p>Хотя <code>troff</code> обеспечивает возможность полностью управлять выходным форматом, ее непросто использовать для набора более сложного материала, такого, как формулы, таблицы и рисунки. Их разметка так же трудна, как разметка страницы. Решить упомянутые выше проблемы можно по-разному. Существуют языки специального назначения для формул, таблиц и рисунков, которые заменяют пакеты команд форматирования. Каждый язык поддерживается отдельной программой, транслирующей его в команды <code>troff</code>. Программы взаимодействуют через программные каналы. Эти препроцессоры служат хорошим примером подхода к проблемам в UNIX: вместо расширения и усложнения troff с ней взаимодействуют отдельные программы. (Конечно, средства развития языка, описанные в гл. 8, были использованы для облегчения реализации.) Ниже описываются две программы: <code>tbl</code>, форматирующая таблицы, и <code>eqn</code>, форматирующая математические выражения.</p>
    <p>Мы постараемся дать вам советы по подготовке документации и применению поддерживающих инструментов. Приводимые здесь примеры войдут в документ, описывающий язык <code>hoc</code>, из гл. 8 и справочного руководства. Сам документ приведен в приложении 2.</p>
   </section>
   <section>
    <title>
     <p>9.1 Пакет макроопределений <code>ms</code></p>
    </title>
    <p>Основная идея, заложенная в пакет макроопределений, состоит в том, что документ описывается в терминах его логических частей названия, заголовков разделов, абзацев, а не в деталях: расстановка пробелов, выбор шрифтов, определение размеров букв. Это спасает вас от рутинной работы и освобождает документ от несущественных подробностей. Фактически вы можете сделать свой документ совершенно иным, использовав другое множество макроопределений с теми же логическими именами. Так, один и тот же документ мог бы последовательно превращаться в технический отчет, доклад на конференции, журнальную статью и главу из книги с помощью одинаковых команд форматирования, интерпретируемых четырьмя разными пакетами макроопределений.</p>
    <p>Входной поток <code>troff</code> представляет собой обычный текст, размеченный командами форматирования, независимо от использования пакета макроопределений. Существуют два вида команд. Команда первого вида состоит из точки в начале строки и следующей за ней одной (двух) буквы либо цифры и, возможно, параметра, как показано ниже:</p>
    <p><code>.PP</code></p>
    <p><code>.ft В</code></p>
    <p><code>Это небольшой абзац, выделенный жирным шрифтом.</code></p>
    <p>Все встроенные команды <code>troff</code> имеют имена, образованные строчными буквами, поэтому по соглашению командам в пакетах даются имена из прописных букв. В нашем примере <code>.PP</code> есть команда <code>ms</code> для абзаца, a <code>.ff</code> — команда <code>troff</code>, вызывающая замену обычного шрифта жирным. (Имена команд, управляющих шрифтами, состоят из прописных букв; шрифты могут быть разными на разных наборных устройствах.)</p>
    <p>Команда второго вида это последовательность символов, начинающаяся с обратной дробной черты <code>\</code>, которая может оказаться в любом месте входного потока. Например, команда <code>\fB</code> также вызывает переключение на жирный шрифт. Она является примером чистой <code>troff</code>; ниже мы коротко ее рассмотрим.</p>
    <p>Для форматирования достаточно вводить команду <code>.PP</code> перед каждым абзацем. В принципе при подготовке большинства документов можно обойтись дюжиной разных команд <code>ms</code>. Так, приложение 2, описывающее <code>hoc</code>, имеет название, имена авторов, резюме, автоматически нумеруемые заголовки разделов и абзацы. Здесь используется всего 14 различных команд, причем некоторые из них парами. В <code>ms</code> документ принимает следующую общую форму:</p>
    <p><code>.TL</code></p>
    <p><code>Название документа (одна или более строк)</code></p>
    <p><code>.AU</code></p>
    <p><code>Имена авторов, одно на строке</code></p>
    <p><code>.AB</code></p>
    <p><code>Резюме, оканчивающееся .AE</code></p>
    <p><code>.AE</code></p>
    <p><code>.NH</code></p>
    <p><code>Нумеруемые заголовки (автоматически)</code></p>
    <p><code>.PP</code></p>
    <p><code>Абзац...</code></p>
    <p><code>.PP</code></p>
    <p><code>Другой абзац...</code></p>
    <p><code>.SH</code></p>
    <p><code>Подзаголовок (нет нумерации)</code></p>
    <p><code>.PP</code></p>
    <p><code>...</code></p>
    <p>Команды форматирования могут появиться в начале строки. Входной текст между командами размещается в произвольной форме: расположение концов строк в нем не существенно, так как <code>troff</code> переносит слова с одной строки на другую, чтобы сделать строки более полными (процесс, называемый <emphasis>заполнением</emphasis>), и расставляет дополнительные пробелы между словами, чтобы выровнять границы (<emphasis>выравнивание</emphasis>). Тем не менее начинать каждое предложение с новой строки считается хорошим стилем; это облегчает последующее редактирование.</p>
    <p>Ниже показано начало непосредственного документа <code>hoc</code>:</p>
    <p><code>.TL</code></p>
    <p><code>Hoc - диалоговый язык для арифметики с плавающей точкой</code></p>
    <p><code>.AU</code></p>
    <p><code>Брайан Керниган</code></p>
    <p><code>Роб Пайк</code></p>
    <p><code>.AB</code></p>
    <p><code>.I Hoc</code></p>
    <p><code>это простой программируемый интерпретатор</code></p>
    <p><code>для выражений с плавающей точкой.</code></p>
    <p><code>Он обеспечивает поток управления в стиле Си,</code></p>
    <p><code>определения функций и обычные</code></p>
    <p><code>числовые встроенные функции,</code></p>
    <p><code>такие как косинус и логарифм.</code></p>
    <p><code>.AE</code></p>
    <p><code>.NH</code></p>
    <p><code>Выражения</code></p>
    <p><code>.PP</code></p>
    <p><code>.I Hoc</code></p>
    <p><code>Это язык выражений, во многом подобный Си:</code></p>
    <p><code>хотя он содержит несколько управляющих операторов,</code></p>
    <p><code>большинство операторов, таких как присваивания,</code></p>
    <p><code>это выражения, чьи значения не принимаются</code></p>
    <p><code>во внимание.</code></p>
    <p><code>...</code></p>
    <p>Команда <code>.I</code> выделяет свой аргумент курсивом или переключает шрифт на курсив, если аргумента нет.</p>
    <p>При использовании пакета макроопределений он специфицируется как аргумент <code>troff</code>:</p>
    <p><code>$ troff -ms hoc.ms</code></p>
    <p>Символы после <code>-m</code> определяют пакет макроопределений.<a l:href="#n17" type="note">[17]</a> После форматирования с помощью <code>ms</code> документ <code>hoc</code> выглядит так:</p>
    <cite>
     <subtitle>Hoc — диалоговый язык для арифметики с плавающей точкой</subtitle>
     <p><emphasis>Брайан Керниган</emphasis></p>
     <p><emphasis>Роб Пайк</emphasis></p>
     <p><emphasis>РЕЗЮМЕ</emphasis></p>
     <p>Hoc — это простой программируемый интерпретатор для выражений с плавающей точкой. Он обеспечивает поток управления в стиле Си, определения функций и обычные числовые встроенные функции, такие как косинус и логарифм.</p>
     <subtitle>1. Выражения</subtitle>
     <p>Hoc — это язык выражений, во многом подобный Си; хотя он содержит несколько управляющих операторов, большинство операторов, таких как присваивания, это выражения, чьи значения не принимаются во внимание.</p>
    </cite>
    <subtitle>Отображения</subtitle>
    <p>Хотя нам и удобно, что <code>troff</code> осуществляет заполнение и выравнивание текста, иногда нежелательно, в частности для программ, выравнивать их границы. Такое форматированное представление называется отображением текста. Команды <code>ms .DS</code> (начало отображения) и <code>.DE</code> (конец отображения) ограничивают текст, благодаря чему он выводится с отступами, но без реорганизации. Посмотрите на следующий фрагмент руководства по <code>hoc</code>, включающий короткое отображение:</p>
    <p><code>.PP</code></p>
    <p><code>.I Hoc</code></p>
    <p><code>Это язык выражений,</code></p>
    <p><code>во многом подобный Си:</code></p>
    <p><code>хотя он содержит несколько управляющих операторов,</code></p>
    <p><code>большинство операторов, таких как присваивания,</code></p>
    <p><code>это выражения, чьи значения не принимаются во внимание.</code></p>
    <p><code>Например, оператор присваивания</code></p>
    <p><code>= присваивает значение его правой части</code></p>
    <p><code>его левому операнду и вырабатывает значение,</code></p>
    <p><code>используемое в многократном присваивании.</code></p>
    <p><code>Грамматика выражений такова:</code></p>
    <p><code>.DS</code></p>
    <p><code>.I</code></p>
    <p><code>выражение: число</code></p>
    <p><code> | переменная</code></p>
    <p><code> | (выражение)</code></p>
    <p><code> | выражение бинарная-операция выражение</code></p>
    <p><code> | унарная-операция выражение</code></p>
    <p><code> | функция(аргументы)</code></p>
    <p><code>.R</code></p>
    <p><code>.DE</code></p>
    <p><code>Числа представляются с плавающей точкой.</code></p>
    <p>Данный фрагмент печатается так:</p>
    <cite>
     <p><emphasis>Hoc</emphasis> — это язык выражений, во многом подобный Си; хотя он содержит несколько управляющих операторов, большинство операторов, таких как присваивания, — это выражения, чьи значения не принимаются во внимание. Например, оператор присваивания = присваивает значение своей правой части левому операнду и вырабатывает значение, используемое в многократном присваивании. Грамматика выражений такова:</p>
     <p><emphasis>выражение: число</emphasis></p>
     <p><emphasis> | переменная</emphasis></p>
     <p><emphasis> | (выражение)</emphasis></p>
     <p><emphasis> | выражение бинарная_операция выражение</emphasis></p>
     <p><emphasis> | унарная_операция выражение</emphasis> </p>
     <p><emphasis> | функция (аргументы)</emphasis></p>
     <p>Числа представляются с плавающей точкой.</p>
    </cite>
    <p>Текст внутри отображения не является ни нормально заполненным, ни выравненным. Далее, если места на текущей странице не хватает, отображаемый материал (и все, что за ним следует) переносится на следующую страницу. Команда <code>.DS</code> обеспечивает несколько флагов, включая <code>L</code> для левого выравнивания, <code>С</code> для индивидуальной центровки каждой строки и <code>В</code> для центровки всего отображения.</p>
    <p>Фрагменты текста в приведенном выше отображении разделены символами табуляции. По умолчанию символы табуляции <code>troff</code> ставятся через каждые полдюйма, а не через восемь пробелов, как обычно. Даже если бы эти символы вставлялись через каждые восемь пробелов, символы табуляции, обрабатываемые <code>troff</code>, не всегда бы появлялись там, где нужно из-за их переменной ширины.</p>
    <subtitle>Смена шрифта</subtitle>
    <p>Макроопределения ms обеспечивают три команды смены шрифта. Команда <code>.R</code> меняет шрифт на латинский (обычный), <code>.I</code> устанавливает курсив, а <code>.B</code> — жирный шрифт:</p>
    <p><code>Этот текст обычный,</code></p>
    <p><code>.I</code></p>
    <p><code>это курсив</code></p>
    <p><code>.R</code></p>
    <p><code>это снова обычный, а</code></p>
    <p><code>.B</code></p>
    <p><code>это жирный шрифт.</code></p>
    <p>При выполнении команд приведенный выше текст превращается в следующий:</p>
    <cite>
     <p>Этот текст обычный, <emphasis>это курсив</emphasis>, это снова обычный, а <strong>это жирный шрифт</strong>.</p>
    </cite>
    <p>Команды <code>.I</code> и <code>.В</code> воспринимают возможные аргументы, причем смена шрифта относится только к аргументу. В <code>troff</code> аргументы, содержащие пробелы, должны быть экранированы, и единственным символом для такой операции служит двойная кавычка <code>"</code>.</p>
    <p><code>Это обычный текст, но</code></p>
    <p><code>.I - это</code></p>
    <p><code>курсив, а</code></p>
    <p><code>.В "эти слова"</code></p>
    <p><code>напечатаны жирным шрифтом.</code></p>
    <p>Данный текст печатается так:</p>
    <cite>
     <p>Это обычный текст, но <emphasis>— это</emphasis> курсив, а <strong>эти слова</strong> напечатаны жирным шрифтом.</p>
    </cite>
    <p>В конечном счете второй аргумент для <code>.I</code> или <code>.B</code>, напечатанный обычным шрифтом, добавляется без пробелов к первому аргументу. Это средство широко используется при выборе шрифта для пунктуации. Обратите внимание на последнюю скобку фразы</p>
    <p><code>(взятые в скобки</code></p>
    <p><code>.I "слова курсивом)"</code></p>
    <p>которая печатается неверно в виде</p>
    <cite>
     <p>(взятые в скобки <emphasis>слова курсивом)</emphasis></p>
    </cite>
    <p>и сравните ее с фразой</p>
    <p><code>(взятые в скобки</code></p>
    <p><code>.I "слова курсивом")</code></p>
    <p>которая печатается верно как</p>
    <cite>
     <p>(взятые в скобки <emphasis>слова курсивом</emphasis>)</p>
    </cite>
    <p>Различные шрифты распознаются программой <code>nroff</code>, но результат оставляет желать лучшего. Символы курсива подчеркнуты и нет жирных литер, хотя некоторые версии <code>nroff</code> изображают жирный шрифт двойной печатью.</p>
    <subtitle>Смешанные команды</subtitle>
    <p>Сноски вводятся с помощью <code>.FS</code> и заканчиваются <code>.FE</code>. Ваше дело определить метку (сноску) в виде звездочки <code>*</code> или крестика <code>†</code>. Такая сноска создается следующим образом:</p>
    <p><code>определяющая метка типа звездочки или крестика. \(dg</code></p>
    <p><code>.FS</code></p>
    <p><code>\(dg подобно этому .FE</code></p>
    <p><code>Эта сноска была создана с помощью ...</code></p>
    <p>Выделенные отступом абзацы, возможно с использованием номера или другой пометки на границе, создаются командой <code>.IP</code>. Сделаем следующее:</p>
    <cite>
     <p>1. Первый небольшой абзац.</p>
     <p>2. Второй абзац, который мы удлиняем, чтобы показать, что отступ во второй строке будет таким же, как в первой.</p>
    </cite>
    <p>Для этого нужен такой входной текст:</p>
    <p><code>.IP(1)</code></p>
    <p><code>Первый небольшой абзац.</code></p>
    <p><code>.IP(2)</code></p>
    <p><code>Второй абзац ...</code></p>
    <p>Команды <code>.PP</code> или <code>.LP</code> (выравненный слева абзац) завершают дело, начатое командой <code>.IP</code>. Аргументом <code>.IP</code> может быть любая строка: введите кавычки, а при необходимости и пробелы. Второй аргумент можно использовать, чтобы определить значение отступа.</p>
    <p>Когда вы работаете с парой команд <code>.KS</code> и <code>.KE</code>, текст должен быть размещен в одном месте; текст, заключенный между этими командами, будет <emphasis>перенесен</emphasis> на новую страницу, если он не разместится весь на текущей странице. Заменив <code>.KS</code> на <code>.KF</code>, вы можете передвинуть текст за последующий текст в верхнюю часть следующей страницы (если его необходимо поместить на одной странице). Все таблицы в книге построены с помощью <code>.KF.</code></p>
    <p>Можно изменить большинство значений <code>ms</code>, принятых по умолчанию, путем установки <emphasis>некоторого числа регистров</emphasis>, являющихся переменными <code>troff</code> и используемых <code>ms.</code> Наиболее часто применяются регистры, управляющие размером текста и интервалом между строками. Нормальным размером текста считается размер в "10 точек", где точка составляет <sup>1</sup>/<sub>72</sub> дюйма (единица, заимствованная из полиграфии). Обычно строки печатаются с 12-точечным разделением (интервалом). Чтобы изменить интервал, например на 9 или 11 точек (как сделано в наших отображениях), присвойте указанные числа регистрам <code>PS</code> и <code>VS</code>:</p>
    <p><code>.nr PS 9</code></p>
    <p><code>.nr VS 11</code></p>
    <p>Другие числовые регистры включают <code>LL</code> для установки длины строки, <code>PI</code> <emphasis>—</emphasis> для определения отступов абзацев и <code>PD</code> <emphasis>—</emphasis> для отделения последних. Это влияет на следующие <code>.PP</code> или <code>.LP</code>.</p>
    <subtitle>Пакет макроопределений <code>mm</code></subtitle>
    <p>Мы не будем подробно рассматривать этот пакет макроопределений, поскольку в целом, а зачастую и в деталях он похож на <code>ms</code>. Пакет <code>mm</code> обеспечивает контроль параметров в расширенном по сравнению с ms диапазоне, обладает большими возможностями (например, автоматически нумеруемые списки) и выдает лучшие сообщения об ошибках. В табл. 9.2 показаны команды <code>mm</code>, эквивалентные командам ms из табл. 9.1.</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>.AB</code></td>
      <td align="left" valign="top">Печатать резюме; оканчивается <code>.AE</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.AU</code></td>
      <td align="left" valign="top">Ввести в следующей строке имя автора; разрешены многократные <code>.AU</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.B</code></td>
      <td align="left" valign="top">Начать печатать "жирный" текст либо выделить жирным шрифтом аргумент, если он есть</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.DS <emphasis>t</emphasis></code></td>
      <td align="left" valign="top">Начать отображать (незаполненный) текст, оканчивающийся <code>.DE</code> <emphasis>t</emphasis>=<code>L</code> (выравнивание по левому краю), <code>С</code> (центрирование), <code>В</code> (центрирование блока)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.EQ s</code></td>
      <td align="left" valign="top">Начать выравнивание <emphasis>s</emphasis> (входной поток <code>eqn</code>); оканчивается <code>.EN</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.FS</code></td>
      <td align="left" valign="top">Начать печатать сноску; оканчивается <code>.FE</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>Л</code></td>
      <td align="left" valign="top">Начать печатать текст, выделенный курсивом, или выделить курсивом аргумент, если он есть</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.IP <emphasis>s</emphasis></code></td>
      <td align="left" valign="top">Сделать абзац с отступом, помеченный <emphasis>s</emphasis></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.KF</code></td>
      <td align="left" valign="top">Печатать слитно часть текста, если необходимо целиком передвинуть на следующую страницу; конец ее <code>.KE</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.KS</code></td>
      <td align="left" valign="top">Печатать статно часть текста на странице; заканчивается <code>.KE</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.LP</code></td>
      <td align="left" valign="top">Печатать новый выравненный слева абзац</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.NH <emphasis>n</emphasis></code></td>
      <td align="left" valign="top">Ввести чистовой заголовок <emphasis>n</emphasis>-го уровня; затем сам заголовок до <code>.PP</code> или <code>.LP</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.PP</code></td>
      <td align="left" valign="top">Сделать новый абзац</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.R</code></td>
      <td align="left" valign="top">Вернуться к обычному шрифту</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.SH</code></td>
      <td align="left" valign="top">Ввести подзаголовок; заголовок следует далее до <code>.PP</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.TL</code></td>
      <td align="left" valign="top">Далее печатать название до следующей команды <code>ms</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.TS</code></td>
      <td align="left" valign="top">Начать печатать таблицу (входной поток <code>tbl</code>); оканчивается <code>.ТЕ</code></td>
     </tr>
    </table>
    <p>Таблица 9.1: <strong>Распространенные команды форматирования <code>ms</code> (см. также справочное руководство по <code>ms(7)</code>)</strong></p>
    <empty-line/>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>.AS</code></td>
      <td align="left" valign="top">Начать печатать резюме; оканчивается <code>.AE</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.AU</code></td>
      <td align="left" valign="top">Задать имя автора</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.B</code></td>
      <td align="left" valign="top">Начать печатать "жирный" текст либо выделить жирным шрифтом аргумент, если он есть</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.DF</code></td>
      <td align="left" valign="top">Задать слитную часть текста, если необходимо ее целиком передвинуть на следующую страницу; оканчивается <code>.DE</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.DS</code></td>
      <td align="left" valign="top">Начать отображать текст; оканчивается <code>.DE</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.EQ</code></td>
      <td align="left" valign="top">Начать выравнивание (входной поток <code>eqn</code>); оканчивается <code>.EN</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.FS</code></td>
      <td align="left" valign="top">Начать печатать сноску; оканчивается <code>.FE</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.I</code></td>
      <td align="left" valign="top">Начать печатать текст, выделенный курсивом, или выделить курсивом аргумент, если он есть</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.Hn "..."</code></td>
      <td align="left" valign="top">Задать нумерованный заголовок n-го уровня "…"</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.HU "..."</code></td>
      <td align="left" valign="top">Задать ненумерованный заголовок "…"</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.P</code></td>
      <td align="left" valign="top">Сделать абзац. Используйте <code>.nr Pt 1</code> один раз для создания абзаца с отступом</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.R</code></td>
      <td align="left" valign="top">Вернуться к обычному шрифту</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.TL</code></td>
      <td align="left" valign="top">Задать заголовок до следующей команды <code>mm</code></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>.TS</code></td>
      <td align="left" valign="top">Начать печатать таблицу (<code>tbl</code> входной поток); оканчивается .<code>ТЕ</code></td>
     </tr>
    </table>
    <p>Таблица 9.2: <strong>Распространенные команды форматирования <code>mm</code></strong></p>
    <empty-line/>
    <cite>
     <subtitle>Упражнение 9.1</subtitle>
     <p>Пропуск завершающей команды типа <code>.AE</code> или <code>.DE</code> обычно ведет к неприятностям. Напишите программу <code>mscheck</code> для обнаружения ошибок во входном потоке <code>ms</code> (или в предпочитаемом вами пакете). <emphasis>Совет</emphasis>: воспользуйтесь <code>awk</code>.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>9.2 Уровень <code>troff</code></p>
    </title>
    <p>На практике приходится иногда выходить за пределы возможностей <code>ms</code>, <code>mm</code> или других пакетов, чтобы реализовать некоторые свойства "чистой" <code>troff</code>. Однако, как и к программированию на языке Ассемблера, прибегать к этому следует в крайних случаях.</p>
    <p>Вероятны три ситуации: доступ к специальным символам, использование встроенных команд замены шрифта и введение нескольких базовых функций форматирования.</p>
    <subtitle>Имена символов</subtitle>
    <p>Доступ к необходимым символам (греческим буквам, например, &#960;, графике вида • и †, разнообразным штрихам и пробелам) несложен, хотя и не вполне систематизирован. Каждый такой символ имеет имя <code>\<emphasis>c</emphasis></code>, где <emphasis>с —</emphasis> одиночный символ, или <code>\(<emphasis>cd</emphasis></code>, где <emphasis>cd —</emphasis> пара символов.</p>
    <p>Программа <code>troff</code> печатает минус в коде ASCII как дефис, а не как '—'. Настоящий минус должен обозначаться через <code>\-</code>, а тире через <code>\(em</code>, называемое "em пунктир", символ "тире".</p>
    <p>В табл. 9.3 перечислены наиболее часто используемые специальные символы; в справочном руководстве по <code>troff</code> их число намного больше (в вашей системе перечень специальных символов может быть иным).</p>
    <table>
     <tr align="left">
      <td align="left" valign="top"><code>-</code></td>
      <td align="left" valign="top">-</td>
      <td align="left" valign="top">Дефис</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\(hy</code></td>
      <td align="left" valign="top">-</td>
      <td align="left" valign="top">Дефис, аналогичный предыдущему</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\-</code></td>
      <td align="left" valign="top">–</td>
      <td align="left" valign="top">Знак "минус", набираемый текущим шрифтом</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\(mi</code></td>
      <td align="left" valign="top">–</td>
      <td align="left" valign="top">Знак "минус", набираемый математическим шрифтом</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\(em</code></td>
      <td align="left" valign="top">—</td>
      <td align="left" valign="top">em тире</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\&amp;</code></td>
      <td align="left" valign="top"/>
      <td align="left" valign="top">Ничего; защищает точку в начале строки</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\blank</code></td>
      <td align="left" valign="top"/>
      <td align="left" valign="top">Неразмножаемый пробел</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\|</code></td>
      <td align="left" valign="top"/>
      <td align="left" valign="top">Неразмножаемый полупробел</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\е</code></td>
      <td align="left" valign="top"/>
      <td align="left" valign="top">Символ экранирования, обычно \</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\(bu</code></td>
      <td align="left" valign="top"/>
      <td align="left" valign="top">Жирная точка</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\(dg</code></td>
      <td align="left" valign="top"/>
      <td align="left" valign="top">Крестик †</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\(*a</code></td>
      <td align="left" valign="top"/>
      <td align="left" valign="top">&#945;, <code>\(*b</code>=&#946;, <code>\(*с</code>=&#958;, <code>\(*p</code>=&#960; и т.д.</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\fX</code></td>
      <td align="left" valign="top"/>
      <td align="left" valign="top">Символ смены шрифта на <emphasis>X</emphasis>; <emphasis>Х</emphasis>=<code>Р</code> — предыдущий (шрифт)</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\f(XX</code></td>
      <td align="left" valign="top"/>
      <td align="left" valign="top">Символ смены шрифта на <emphasis>XX</emphasis></td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\sn</code></td>
      <td align="left" valign="top"/>
      <td align="left" valign="top">Символ смены размера шрифта на <emphasis>n</emphasis>; <emphasis>n</emphasis>=0 — предыдущий</td>
     </tr>
     <tr align="left">
      <td align="left" valign="top"><code>\s+-n</code></td>
      <td align="left" valign="top"/>
      <td align="left" valign="top">Относительная замена размера шрифта</td>
     </tr>
    </table>
    <p>Таблица 9.3: <strong>Некоторые последовательности специальных символов <code>troff</code></strong></p>
    <empty-line/>
    <p>В ряде случаев требуется, чтобы <code>troff</code> <emphasis>не</emphasis> интерпретировала символ, особенно обратную дробную черту или точку в начале строки. Два наиболее часто применяемых "отменяющих" символа — <code>\e</code> и <code>\&amp;</code>. Последовательность <code>\е</code> гарантированно печатается как обратная дробная черта, не интерпретируется и используется для получения такого символа в выходном потоке. С другой стороны, <code>\&amp;</code> не несет никакой смысловой нагрузки: это пробел нулевой ширины. Главное назначение этой комбинации — заставить <code>troff</code> не интерпретировать точки в начале строк. Мы задействовали <code>\е</code> и <code>\&amp;</code> здесь несколько раз. Например, фрагмент <code>ms</code> в начале главы был напечатан как</p>
    <p><code>\&amp;.TL</code></p>
    <p><code>.I "Название документа"</code></p>
    <p><code>\&amp;.AU</code></p>
    <p><code>.I "Имя автора"</code></p>
    <p><code>\&amp;.AB</code></p>
    <p><code>\&amp;...</code></p>
    <p><code>...</code></p>
    <p>Конечно, этот фрагмент был напечатан следующим образом:</p>
    <cite>
     <p>\e&amp;.TL</p>
     <p>\&amp;.I "Название документа"</p>
     <p>\е&amp; .AU</p>
     <p>...</p>
    </cite>
    <p>и вы можете себе представить, как в свою очередь был напечатан последний фрагмент.</p>
    <p>Другой специальный символ, <emphasis>"неразмножаемый" пробел</emphasis>, появляется изредка: это символ <code>\</code>, за которым следует пробел. Как правило, <code>troff</code> размножает обычный пробел, чтобы выровнять границы, но неразмножаемый пробел не позволяет "растягивать" строку. Он подобен любому другому символу и имеет фиксированную ширину. Его также можно использовать для передачи нескольких слов единым аргументом:</p>
    <p><code>.I Название\ документа</code></p>
    <subtitle>Смена шрифта и размера символов текста</subtitle>
    <p>В большинстве случаев замена шрифтов и форматов может быть сделана с помощью начинающей строку макрокоманды типа <code>.I</code>, но иногда их замена должна осуществляться и в строке. В частности, символ конца строки разделяет слова, поэтому если требуется сменить шрифт в середине слова, макрокоманду нельзя использовать. С помощью <code>troff</code> можно решить эту проблему (отметим, что именно <code>troff</code>, а не пакет ms обеспечивает такую возможность).</p>
    <p>Встроенные (in-line) команды troff вводит с использованием символа <code>\</code>. Наиболее часто применяются команды <code>\f</code> для смены шрифта и <code>\s</code> для смены формата.</p>
    <p>Шрифт, заменяемый командой <code>\f</code>, определяется символом, следующим непосредственно за <code>f</code>:</p>
    <p><code>a\fBпро\fIизвольное \fR \fI мно\fBжество \fIшрифтов\fP</code></p>
    <p>Это выводится как</p>
    <cite>
     <p><strong>про</strong><emphasis>извольное мно</emphasis><strong>жество</strong> <emphasis>шрифтов</emphasis></p>
    </cite>
    <p>При смене шрифта <code>\fP</code> возвращает нас к предыдущему шрифту — тому, который был до последнего переключения. (Есть только один предыдущий шрифт, т.е. стека нет.)</p>
    <p>Некоторые шрифты имеют двухсимвольные имена. Они специфицируются форматом <code>\f(XX</code>, где <code>XX</code> — имя шрифта. Например, шрифт, которым напечатаны программы в нашей книге, называется <code>CW</code> (курьер постоянной ширины), поэтому <code>keyword</code> пишется так:</p>
    <p><code>\f\(CWkeyword \fP</code></p>
    <p>Очевидно, печатать это довольно неудобно, поэтому мы ввели расширение <code>ms</code> макрокоманду <code>.CW</code>, так что теперь нет необходимости печатать или читать символы <code>\</code>.</p>
    <p>Воспользуемся указанным расширением, чтобы набирать слова внутри строки, такие, как <code>troff</code>, следующим образом:</p>
    <p><code>The</code></p>
    <p><code>.CW troff</code></p>
    <p><code>formatter ...</code></p>
    <p>Решения о форматировании, определяемые макрокомандами, также легко потом поменять.</p>
    <p>Смена размера шрифта осуществляется последовательностью <code>\sn</code>, где <code>n</code> — одна или две цифры, определяющие новый размер: <code>\s8</code> переключает на восьмиточечный размер. В принципе можно выполнять относительные замены, предпосылая размеру плюс или минус. Например, слова можно напечатать в SMALL CAPS, введя</p>
    <p><code>\s-2SMALL CAPS\s0</code></p>
    <p>Комбинация <code>\s0</code> предписывает возвратить размер к его предыдущему значению. Это аналог <code>\fP</code>, но, следуя традиции <code>troff</code>, она не записывается как <code>\sP</code>. Наши расширения <code>ms</code> включают макрокоманду <code>.UC</code> (прописные буквы) для такого рода работы.</p>
    <subtitle>Основные команды <code>troff</code></subtitle>
    <p>Даже располагая хорошим пакетом макрокоманд, мы должны знать достаточно много команд <code>troff</code> для управления пробелами и заполнением, для установки позиций табуляции и т.п. Команда <code>.br</code> вызывает "обрыв", т.е. следующий вводимый за <code>.br</code> текст окажется на новой выходной строке. Это явление можно использовать, например, чтобы расщепить длинное название в подходящем месте:</p>
    <p><code>.TL</code></p>
    <p><code>Hoc - Диалоговый язык</code></p>
    <p><code>.BR</code></p>
    <p><code>для арифметики с плавающей точкой</code></p>
    <p><code>...</code></p>
    <p>Команда <code>.nf</code> отключает нормальное заполнение выходных строк; каждая строка ввода переходит непосредственно в одну выходную строку. Команда <code>.fi</code> снова включает процедуру заполнения. Команда <code>.се</code> центрирует следующую строку.</p>
    <p>Команда <code>.bp</code> начинает новую страницу. Команда <code>.sp</code> вызывает появление на выходе одной пустой строки. За командой <code>.sp</code> может следовать аргумент, чтобы задать число пустых строк или число пробелов:</p>
    <p><code>.sp 3    </code>Оставить 3 пустых строки</p>
    <p><code>.sp .5   </code>Оставить пустые полстроки</p>
    <p><code>.sp 1.51 </code>Отступить на 1,5 дюйма</p>
    <p><code>.sp 3p   </code>Вставить 3 позиции</p>
    <p><code>.sp 3.1с </code>Оставить 3,1 сантиметра</p>
    <p>Лишнее пространство в нижней части страницы не играет роли, поэтому <code>.sp</code> с большим аргументом эквивалентно <code>bp</code>.</p>
    <p>Команда <code>.ta</code> устанавливает позиции табуляции (которые инициализируются каждые полдюйма). Команда</p>
    <p><code>.ta <emphasis>n n n</emphasis> ...</code></p>
    <p>расставляет позиции табуляции на определяемых расстояниях от левой границы. Как и для <code>.sp</code>, каждое число <emphasis>n</emphasis> измеряется в дюймах, если за ним следует '<code>i</code>'. Позиция табуляции с суффиксом <code>R</code> вызывает правое выравнивание текста на очередной позиции табуляции, <code>С</code> вызывает центрированную табуляцию.</p>
    <p>Команда <code>.ps n</code> устанавливает значение <emphasis>n</emphasis> размера шрифта; команда <code>.ft X</code> устанавливает шрифт <emphasis>X</emphasis>. Правила для увеличения размеров и возвращения к предыдущему значению те же самые, что и для <code>\s</code> и <code>\f</code>.</p>
    <subtitle>Определение макрокоманд</subtitle>
    <p>Определение макрокоманд во всей полноте потребовало бы от нас более детального изучения <code>troff</code>, чем это необходимо. Поэтому мы ограничимся здесь рассмотрением нескольких примеров. В частности, определение для <code>.CW</code> имеет вид</p>
    <p><code>.de CW              </code>Начать определение</p>
    <p><code>\&amp;\f (CW\\$1\fP\\$2 </code>Смена шрифта для первого аргумента</p>
    <p><code>...                 </code>Конец определения</p>
    <p>Комбинация <code>\$<emphasis>n</emphasis></code> дает значение <emphasis>n</emphasis>-го аргумента при вызове макрокоманды: оно пусто в случае отсутствия <emphasis>n</emphasis>-го аргумента. Двойной символ <code>\\</code> откладывает вычисление <code>\$<emphasis>n</emphasis></code> на время определения макрокоманды. Комбинация <code>\&amp;</code> не позволяет интерпретировать аргумент как команду <code>troff</code> в том случае, если он начинается с точки, как показано ниже:</p>
    <p><code>.CW .sp</code></p>
   </section>
   <section>
    <title>
     <p>9.3 Препроцессоры <code>tbl</code> и <code>eqn</code></p>
    </title>
    <p>Программа <code>troff</code> — большая и сложная, и поэтому модифицировать ее для того, чтобы она выполняла новую задачу, нелегко. Соответственно разработка программ для набора математических выражений и таблиц требует другого подхода, а именно создания специальных языков, реализованных отдельными программами <code>eqn</code> и <code>tbl</code>, действующих как процессоры для <code>troff</code>. Программа <code>troff</code> по существу представляет язык Ассемблера для наборной машины, a <code>eqn</code> и <code>tbl</code> компилируют для нее код.</p>
    <p>Вначале появилась <code>eqn</code>. Это было первое применение <code>yacc</code> не для целей программирования<a l:href="#n18" type="note">[18]</a>. Программа <code>tbl</code>, разработанная позднее, аналогична <code>eqn</code>, хотя и имеет независимый синтаксис; <code>tbl</code> не использует <code>yacc</code>, так как ее грамматика достаточно проста.</p>
    <p>Средства программных каналов UNIX предполагают строгое разделение на отдельные программы. Кроме разбиения на части (что так или иначе необходимо, поскольку <code>troff</code> уже достигла максимального размера для PDP-11), программные каналы сужают круг взаимодействия между частями программы и между разрабатывающими их программистами. Последнее особенно важно: чтобы сделать препроцессор, не нужно "залезать" в исходную программу. Далее программные каналы позволяют не создавать большие промежуточные файлы при условии, что компоненты намеренно запускаются отдельно с целью отладки.</p>
    <p>Однако организация взаимодействия программ через конвейеры связана с некоторыми проблемами. Отчасти снижается быстродействие, поскольку увеличивается объем ввода и вывода: обычно и <code>eqn</code>, и <code>tbl</code> дают расширение выходного потока по отношению к входному в отношении 8:1. Еще более существенно, что информация идет только в одном направлении. Например, нет способа определения текущего размера шрифта, что создаст неудобства в пользовании языком. И, наконец, трудно обеспечить сообщения об ошибках, так как иногда трудно связать диагностику из <code>troff</code> с <code>eqn</code> и <code>tbl</code>. Тем не менее преимущества разделения значительно перекрывают недостатки, поэтому было написано несколько препроцессоров, основанных на этой модели.</p>
    <subtitle>Таблицы</subtitle>
    <p>Обсудим кратко работу <code>tbl</code> и прежде всего таблицу операций по документации к <code>hoc</code>. <code>tbl</code> читает свои входные файлы или стандартный входной поток и преобразует текст между командами <code>.TS</code> (начало таблицы) и <code>.ТЕ</code> (конец таблицы) в команды <code>troff</code>, печатающие таблицу, выравнивающие столбцы и обеспечивающие все типографские атрибуты. Строки <code>.TS</code> и <code>.ТЕ</code> тоже копируются, поэтому пакет макроопределений выдает для них подходящие определения с тем, например, чтобы можно было помещать таблицу на одной странице и отделять ее от окружающего текста.</p>
    <p>При формировании сложных таблиц вам, конечно, придется обращаться к справочному руководству по <code>tbl</code>. Хотя для уяснения основных принципов работы вполне достаточно приведенного ниже примера (из документации по <code>hoc</code>).</p>
    <p><code>.TS</code></p>
    <p><code>center, box;</code></p>
    <p><code>с s</code></p>
    <p><code>lfCW 1</code></p>
    <p><code>\fВТаблица 1:\fP Операции по порядку уменьшения приоритета</code></p>
    <p><code>.sp.5</code></p>
    <p><code>^       возведение в степень (\s-1FORTRAN\s0 **) правоассоциативна</code></p>
    <p><code>!\-     одноместные логическое и арифметическое отрицания</code></p>
    <p><code>* /     умножение, деление</code></p>
    <p><code>+\-     сложение, вычитание</code></p>
    <p><code>&gt; &gt;=    операции отношения: больше, больше или равно &lt; &lt;= меньше, меньше или равно</code></p>
    <p><code>\&amp;== != равно, не равно (все отношения одинакового приоритета)</code></p>
    <p><code>&amp;&amp;      логическое И (оба операнда всегда вычисляются)</code></p>
    <p><code>||      логическое ИЛИ (оба операнда всегда вычисляются)</code></p>
    <p><code>\&amp;=     присваивание, правоассоциативна</code></p>
    <p><code>.ТЕ</code></p>
    <p>В результате мы получаем следующую таблицу:</p>
    <cite>
     <subtitle>Таблица 1: Операции по порядку уменьшения приоритета</subtitle>
     <table>
      <tr align="left">
       <td align="left" valign="top"><code>^</code></td>
       <td align="left" valign="top">возведение в степень (FORTRAN **) правоассоциативна</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>! -</code></td>
       <td align="left" valign="top">одноместные логическое и арифметическое отрицания</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>* /</code></td>
       <td align="left" valign="top">умножение, деление</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>+ -</code></td>
       <td align="left" valign="top">сложение, вычитание</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>&gt; &gt;=</code></td>
       <td align="left" valign="top">операции отношения: больше, больше или равно</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>&lt; &lt;=</code></td>
       <td align="left" valign="top">меньше, меньше или равно</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>== !=</code></td>
       <td align="left" valign="top">равно, не равно (все отношения одинакового приоритета)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>&amp;&amp;</code></td>
       <td align="left" valign="top">логическое И (оба операнда всегда вычисляются)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>!!</code></td>
       <td align="left" valign="top">логическое ИЛИ (оба операнда всегда вычисляются)</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>=</code></td>
       <td align="left" valign="top">присваивание, правоассоциативна</td>
      </tr>
     </table>
    </cite>
    <p>Слова перед точкой с запятой описывают глобальные свойства таблицы: центрировать по горизонтали на странице и заключить в рамку. Другие средства включают <code>doublebox</code> (сделать двойную рамку), <code>allbox</code> (включить каждый элемент в рамку) и <code>expand</code> (расширить таблицу на формат страницы).</p>
    <p>Следующие строки до точки описывают формат различных секций таблицы. Первая спецификация служит для первой строки таблицы, вторая для второй, последняя для всех остальных строк. В табл. 1 вы видите только две строки спецификаций, поэтому вторая спецификация применяется к каждой строке таблицы посте первой. Символы формата для элементов центрированных в столбце, <code>с</code>, <code>r</code> и <code>l</code> для правого и левого выравнивания и <code>n</code> — для выравнивания чисел по десятичной точке. Символ <code>S</code> определяет столбец с промежутком; в нашем случае '<code>c s</code>' означает центровку названия над всей таблицей путем задания размера второго столбца так же, как и первого. Для столбца можно определить шрифт. Спецификация <code>tbl lfCW</code> позволяет печатать выравненный по левому краю столбец шрифтом CW<code>.</code></p>
    <p>Текст таблицы следует за информацией для форматирования. Символы табуляции разделяют столбцы и некоторые команды troff, например .<code>sp</code>, которые уместны внутри таблиц. (Отметим пару вхождений <code>\&amp;</code>: незащищенный передний символ <code>-</code> и знак <code>=</code> в столбцах указывают <code>tbl</code> на необходимость располагать строки таблицы в этой точке.)</p>
    <p>Программа <code>tbl</code> строит более широкий набор таблиц, чем показано в примере: текст может помещаться в рамки, могут вертикально выравниваться заголовки столбцов и т.д. Самый легкий способ использовать <code>tbl</code> для создания сложных таблиц обратиться к справочному руководству по UNIX (том 2A) и применить перечисленные в нем команды.</p>
    <subtitle>Математические выражения</subtitle>
    <p>Второй препроцессор <code>eqn</code> превращает язык, описывающий математические выражения, в команды <code>troff</code>, чтобы эти выражения печатать. Препроцессор автоматически обрабатывает смены шрифта и формата и, кроме того, предоставляет имена для стандартных математических символов. Входной текст для <code>eqn</code> обычно находится между строками <code>.EQ</code> и <code>.EN</code>, аналогично командам <code>tbl .TS</code> и <code>.ТЕ</code>. Например,</p>
    <p>.<code>EQ</code></p>
    <p><code>x sub i</code></p>
    <p><code>.EN</code></p>
    <p>выдаёт <emphasis>x</emphasis><sub><emphasis>i</emphasis></sub>. Если используется пакет <code>ms</code>, уравнение печатается как "отображение", а возможный аргумент <code>.EQ</code> определяет номер уравнения. Например, формула интеграла Коши</p>
    <image l:href="#img_8.png"/>
    <p>записывается как</p>
    <p><code>.EQ (9.1)</code></p>
    <p><code>f( zeta ) ~=~ 1 over {2 pi i} int from С</code></p>
    <p><code>f(z) over {z - zeta} dz</code></p>
    <p><code>.EN</code></p>
    <p>В основу языка <code>eqn</code> и положен способ чтения вслух математических формул. Единственное различие между "разговорной" математикой и входным текстом <code>eqn</code> состоит в том, что скобки <code>{ }</code> отменяют заданные по умолчанию правила предшествования языка, однако обычные скобки специального смысла не имеют. Пробелы тем не менее важны. Заметим, что первое вхождение <code>zeta</code> в примере, приведенном выше, окружено пробелами: ключевые слова, такие, как <code>zeta</code> и <code>over</code>, распознаются только тогда, когда они окружены пробелами или скобками, но ни те, ни другие в выходной текст не попадают. Чтобы обеспечить пробелы в выходном потоке, используйте символ <code>~</code>, как показано в примере (<code>~=~</code>). Для получения скобок используйте "<code>{</code>" и "<code>}</code>".</p>
    <p>Существует несколько классов ключевых слов <code>eqn</code>. Греческие буквы записываются прописными и строчными: <code>lambda</code> и <code>LAMBDA</code> (&#955; и &#923;). Другие математические символы имеют имена, такие как <code>sum</code>, <code>int</code>, <code>infty</code>, <code>grad</code>: &#931;, &#8747;, &#8734;, &#8711;. Есть знаки позиции, например <code>sub</code>, <code>sup</code>, <code>from</code>, <code>to</code>, <code>and</code>, <code>over</code>:</p>
    <p>Эта формула выводится так:<image l:href="#img_9.png"/></p>
    <p><code>sum from i=0 to infinity x sub i sup 2~&#8594;~1 over {2pi}</code></p>
    <p>Существуют знаки операций типа <code>sqrt</code>, расширяющие скобки, фигурные скобки и т.д. Программа <code>eqn</code>, кроме того, позволяет создавать из объектов столбцы и матрицы. Предусмотрены команды для управления шрифтами и позициями, если те, которые установлены по умолчанию, не подходят.</p>
    <p>Часто приходится помещать небольшие математические выражения, такие, как log<sub>10</sub>(x), в тело текста, а не в отображение. Ключевое слово <code>eqn</code> <code>delim</code> определяет пару символов для выделения подобных выстроенных выражений. Символы, задаваемые в качестве левого и правого ограничителей, обычно одинаковы: часто применяется знак доллара <code>$</code>. Но поскольку <code>hoc</code> использует <code>$</code> для аргументов, в нашем примере мы употребили <code>@</code>. Символ <code>%</code> тоже удобен как ограничитель, но других символов избегайте: многие из них имеют специальные назначения в различных программах, поэтому вы можете спровоцировать непредсказуемое поведение <code>eqn</code> (именно так у нас и получилось с этим разделом).</p>
    <p>Итак, после обозначения</p>
    <p><code>.EQ</code></p>
    <p><code>delim @ @</code></p>
    <p><code>.EN</code></p>
    <p>можно напечатать встроенное выражение <image l:href="#img_10.png"/> в виде</p>
    <p><code>@ sum from i == 0 to infinity x sub i @ can be printed.</code></p>
    <p>Встроенные выражения используются для вывода формул в таблице (см. пример из документации по <code>hoc</code>):</p>
    <p><code>.TS</code></p>
    <p><code>center,box</code></p>
    <p><code>css</code></p>
    <p><code>lfCWn1.</code></p>
    <p><code>\fВТаблица 3:\fР Встроенные константы</code></p>
    <p><code>.sp.5</code></p>
    <p><code>DEG  57.29577951308232087680 @180/pi@, градусы и радианы</code></p>
    <p><code>E     2.71828182845904523536 @e@, основание натурального логарифма</code></p>
    <p><code>GAMMA 0.57721566490153286060 @gamma@, константа Эйлера-Масчерони</code></p>
    <p><code>PHI   1.61803398874989484820 @(sqrt5+1)/2@, золотое сечение</code></p>
    <p><code>PI    3.14159265358979323846 @pi@, круговое трансцендентное число</code></p>
    <p><code>.ТЕ</code></p>
    <p>Из этой таблицы, кроме того, видно, как строки <code>tbl</code> помещают десятичные точки в числовых (<code>n</code>) столбцах. Результат показан ниже.</p>
    <cite>
     <subtitle>Таблица 3: <strong>Встроенные константы</strong></subtitle>
     <table>
      <tr align="left">
       <td align="left" valign="top"><code>DEG</code></td>
       <td align="left" valign="top">57.29577951308232087680</td>
       <td align="left" valign="top">180/&#960;, градусы на радианы</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>E</code></td>
       <td align="left" valign="top">2.71828182845904523536</td>
       <td align="left" valign="top"><emphasis>е</emphasis>, основание натуральных логарифмов</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>GAMMA</code></td>
       <td align="left" valign="top">0.57721566490153286060</td>
       <td align="left" valign="top">&#947;, константа Эйлера-Масчерони</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>PHI</code></td>
       <td align="left" valign="top">1.61803398874989484820</td>
       <td align="left" valign="top">(&#8730;5 + 1)/2, золотое сечение</td>
      </tr>
      <tr align="left">
       <td align="left" valign="top"><code>PI</code></td>
       <td align="left" valign="top">3.14159265358979323846</td>
       <td align="left" valign="top">&#960;, круговое трансцендентное число</td>
      </tr>
     </table>
    </cite>
    <p>И, наконец, поскольку <code>eqn</code> выделяет курсивом любую строку букв, которые она не распознает, довольно просто выделять обычные слова курсивом. Последовательность <code>@Word@</code> например, печатается как <emphasis>Word</emphasis>. Но будьте внимательны: <code>eqn</code> распознает некоторые обычные символы (такие, как <code>from</code> и <code>to</code>) и специальным образом их рассматривает: она "глотает" пробелы, поэтому указанный прием следует применять с осторожностью.</p>
    <subtitle>Получение выходного потока</subtitle>
    <p>Как только ваш документ готов, вы должны соединить все препроцессоры и <code>troff</code> в цепочку, чтобы получить выходной поток. Порядок команд следующий: <code>tbl</code>, <code>eqn</code>, <code>troff</code>. Если вы просто используете <code>troff</code>, то печатайте</p>
    <p><code>$ troff -ms <emphasis>имена_файлов</emphasis></code> (или <code>-mm</code>)</p>
    <p>Иначе вам придется задать аргумент <code><emphasis>имена_файлов</emphasis></code> первой команде в цепочке и дать остальным командам читать их стандартный входной поток, как показано ниже:</p>
    <p><code>$ eqn <emphasis>имена_файлов</emphasis> | troff -ms</code></p>
    <p>или</p>
    <p><code>$ tbl <emphasis>имена_файлов</emphasis> | eqn | troff -ms</code></p>
    <p>Неудобно следить за тем из препроцессоров, который действительно должен печатать какой-то отдельный документ. Мы сочли уместным написать программу <code>doctype</code>, обеспечивающую вывод соответствующей последовательности команд:</p>
    <p><code>$ doctype ch9.*</code></p>
    <p><code>cat ch9.1 ch9.2 ch9.3 ch9.4 | pic | tbl | eqn | troff -ms</code></p>
    <p><code>$ doctype hoc.ms</code></p>
    <p><code>cat hoc.ms | tbl | eqn | troff -ms</code></p>
    <p><code>$</code></p>
    <p>Программа <code>doctype</code> реализована с помощью инструментов, рассмотренных в гл. 4. В частности, программа <code>awk</code> отыскивает последовательность команд, используемую препроцессорами, и печатает строку команд, которые нужно вызвать, чтобы отформатировать документ. Она также находит команду <code>.PP</code> (абзац) для форматирования пакетом запросов <code>ms</code>.</p>
    <p><code>$ cat doctype</code></p>
    <p><code># doctype: synthesize proper command line for troff</code></p>
    <p><code>echo -n "cat $* | "</code></p>
    <p><code>egrep -h (EQ|TS|\[|PS|IS|PP)' $* |</code></p>
    <p><code>sort -u |</code></p>
    <p><code>awk '</code></p>
    <p><code> /^\.PP/ { ms++ }</code></p>
    <p><code> /^\.EQ/ { eqn++ }</code></p>
    <p><code> /^\.TS/ { tbl++ }</code></p>
    <p><code> /^\.PS/ { pic++ }</code></p>
    <p><code> /^\.IS/ { ideal++ }</code></p>
    <p><code> /^\.\[/ { refer++ }</code></p>
    <p><code> END {</code></p>
    <p><code>  if (refer &gt; 0) printf "refer | "</code></p>
    <p><code>  if (pic &gt; 0) printf "pic | "</code></p>
    <p><code>  if (ideal &gt; 0) printf "ideal | "</code></p>
    <p><code>  if (tbl &gt; 0) printf "tbl | "</code></p>
    <p><code>  if (eqn &gt; 0) printf "eqn | "</code></p>
    <p><code>  printf "troff "</code></p>
    <p><code>  if (ms &gt; 0) printf "-ms"</code></p>
    <p><code>  printf "\n"</code></p>
    <p><code> } '</code></p>
    <p><code>$</code></p>
    <p>(Флаг <code>-h</code> заставляет ее подавлять заголовки имен файлов на каждой строке: к сожалению, этот аргумент есть не во всех версиях системы.) При сканировании входного потока собирается информация о том, какие компоненты используются. После просмотра входной поток обрабатывается в требуемой последовательности для печати выходного текста. В формировании документов <code>troff</code> со стандартными препроцессорами есть специфика, и главная задача состоит в том, чтобы заставить "думать" об этом саму машину.</p>
    <p>Программа <code>doctype</code> в нашем примере подобна <code>bundle</code>-программе, которая создает программу. Однако в таком виде она требует от пользователя вновь вводить строку для <code>shell</code>. В одном из приводимых ниже упражнений вам предлагается это исправить.</p>
    <p>Когда дело дойдет до запуска реальных команд <code>troff</code>, не забывайте, что поведение программы зависит от системы: на некоторых установках она управляет наборным устройством непосредственно, в то время как на других выдает в стандартный выходной поток информацию, которая должна быть послана на наборное устройство отдельной программой.</p>
    <p>Между прочим, в новой версии этой программы не предусмотрена программа <code>egrep</code> или <code>sort</code>; <code>awk</code> сама просматривает весь входной поток. Для больших документов такой вариант оказывается слишком медленным, поэтому для ускорения поиска мы добавили <code>egrep</code> и затем <code>sort -u</code>, чтобы избавиться от дублирования. При построении типичных документов накладные расходы по созданию двух дополнительных разбирающих данные процессов меньше, чем запуск <code>awk</code> в тех же целях с большим объемом входного текста.</p>
    <p>В качестве иллюстрации сравним <code>doctype</code> с версией, только запускающей <code>awk</code> применительно к содержимому данной главы (около 52 000 символов):</p>
    <p><code>$ time awk '... doctype without egrep ...' ch9.*</code></p>
    <p><code>cat ch9.1 ch9.2 ch9.3 ch9.4 | pic | tbl | eqn | troff -ms</code></p>
    <empty-line/>
    <p><code>real 31.0</code></p>
    <p><code>user 8.9</code></p>
    <p><code>sys 2.8</code></p>
    <p><code>$ time doctype ch9*</code></p>
    <p><code>cat ch9.1 ch9.2 ch9.3 ch9.4 | pic | tbl | eqn | troff -ms</code></p>
    <empty-line/>
    <p><code>real 7.0</code></p>
    <p><code>user 1.0</code></p>
    <p><code>sys 2.3</code></p>
    <p><code>$</code></p>
    <p>Сравнение, очевидно, в пользу версии с тремя процессами. (Работа была выполнена в однопользовательском режиме; соотношение значений времени показало бы даже более значительное преимущество версии <code>egrep</code> и при повышенной нагрузке на систему.) Отметим, что, прежде чем начать оптимизацию, мы получили сначала простую работающую версию.</p>
    <cite>
     <subtitle>Упражнение 9.2</subtitle>
     <p>Как мы сформатировали эту главу?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 9.3</subtitle>
     <p>Если вашим ограничителем для <code>eqn</code> является знак доллара, то как вы получите этот знак в выходном потоке? Подсказка: исследуйте кавычки и предопределенные слова <code>eqn</code>.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 9.4</subtitle>
     <p>Почему команда</p>
     <p><code>$ doctype <emphasis>имена_файлов</emphasis></code></p>
     <p>не выполняется? Модифицируйте <code>doctype</code> так, чтобы запускать команду, полученную в результате, вместо того, чтобы ее печатать.</p>
    </cite>
    <cite>
     <subtitle>Упражнение 9.5</subtitle>
     <p>Важны ли накладные расходы на добавочную команду <code>cat</code> в <code>doctype</code>? Перепишите <code>doctype</code>, чтобы избавиться от дополнительного процесса. Какая версия проще?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 9.6</subtitle>
     <p>Что лучше: использовать <code>doctype</code> или писать файл <code>shell</code>, содержащий команды, для форматирования конкретного документа?</p>
    </cite>
    <cite>
     <subtitle>Упражнение 9.7</subtitle>
     <p>Поэкспериментируйте с различными комбинациями <code>grep</code>, <code>egrep</code>, <code>fgrep</code>, <code>sed</code>, <code>awk</code> и <code>sort</code>, чтобы повысить быстродействие doctype.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>9.4 Справочник</p>
    </title>
    <p>Основной документацией для команды является обычно справочная страничка (называемая далее справочником) одностраничное описание в справочном руководстве по UNIX (см. рис. 9.2). Справочник хранится в стандартном каталоге, как правило, в <code>/usr/man</code>, в подкаталоге, нумерованном в соответствии с разделом руководства. Например, наш справочник для <code>hoc</code> хранится в <code>/usr/man/man1/hoc.1</code>.</p>
    <p>Справочники печатаются с помощью команды <code>man(1)</code>, т.е. файла <code>shell</code>, который запускает <code>nroff -man</code>, поэтому <code>man hoc</code> печатает справочник <code>hoc</code>. Если одно и то же имя появляется в нескольких разделах, как само man (раздел 1 описывает команду, тогда как раздел 7 описывает макрокоманды), то раздел можно определить для <code>man</code> как</p>
    <p><code>$ man 7 man</code></p>
    <p>В результате печатается только описание макрокоманд пакета <code>man</code>. По умолчанию принято печатать все справочники с определенным именем, использующим <code>nroff</code>, но <code>man -t</code> порождает справочники для наборного устройства с помощью <code>troff</code>.</p>
    <p>Автор справочника создает файл в соответствующем подкаталоге <code>/usr/man</code>. Чтобы печатать справочник, команда <code>man</code> вызывает <code>nroff</code> или <code>troff</code> с пакетом макроопределений; это можно увидеть, отыскав команду <code>man</code> для вызовов форматирующей программы. Мы получили бы такой результат:</p>
    <p><code>$ grep roff `which man`</code></p>
    <p><code>nroff $opt -man $all ;;</code></p>
    <p><code>neqn $all | nroff $opt -man ;;</code></p>
    <p><code>troff $opt -man $all ;;</code></p>
    <p><code>troff -t $opt -man $all | tc ;;</code></p>
    <p><code>eqn $all troff $opt -man ;;</code></p>
    <p><code>eqn $all troff -t $opt -man | tc ;;</code></p>
    <p><code>$</code></p>
    <p>Разнообразие достигается применением флагов: <code>nroff</code> или <code>troff</code>, запускается или нет <code>eqn</code> и т.д. Справочник по макрокомандам, вызываемый <code>troff -man</code>, определяет команды <code>troff</code>, форматирующие в стиле данного руководства. В принципе они аналогичны макрокомандам <code>ms</code>, но есть и различия, особенно в установке названия и командах смены шрифта. Макрокоманды кратко документированы в <code>man(7)</code>, но основные из них легко запоминаются. Разметка справочника такова:</p>
    <p><code>.TH COMMAND номер раздела</code></p>
    <p><code>.SH NAME</code></p>
    <p><code>команда \- краткое описание функций</code></p>
    <p><code>.В команда возможные аргументы</code></p>
    <p><code>.SH DESCRIPTION</code></p>
    <p><code>Подробное объяснение команд и флагов.</code></p>
    <p><code>Абзацы вводятся .PP.</code></p>
    <p><code>.PP</code></p>
    <p><code>Это новый абзац.</code></p>
    <p><code>.SH FILES</code></p>
    <p><code>Файлы, используемые командой, например, passwd(1)</code></p>
    <p><code>упоминает /etc/passwd</code></p>
    <p><code>.SH "SEE ALSO."</code></p>
    <p><code>Ссылки к связанным документам, включая другие справочники</code></p>
    <p><code>.SH DIAGNOSTICS</code></p>
    <p><code>Описание некоторого необычного выходного потока</code></p>
    <p><code>(например, см. cmp(1))</code></p>
    <p><code>.SH BUGS</code></p>
    <p><code>Неожиданные черты (не всегда ошибки; см. ниже)</code></p>
    <p>Если какой-то раздел пуст, его заголовок опускается. Строка. <code>.TH</code> и разделы <code>NAME</code>, <code>SYNOPSIS</code>, <code>DESCRIPTION</code> не обязательны. Строка</p>
    <p><code>.TH COMMAND номер раздела</code></p>
    <p>называет команду и определяет номер раздела. Различные строки <code>.SH</code> идентифицируют разделы справочника. Разделы <code>NAME</code> и <code>SYNOPSIS</code> являются специальными; остальные содержат обычный текст. Раздел <code>NAME</code> называет <emphasis>команду</emphasis> (на этот раз строчными буквами) и дает ее описание в одной строке, а раздел <code>SYNOPSIS</code> называет флаги, но не описывает их. Как и в любом разделе, входной текст имеет произвольную форму, поэтому смену шрифта можно определять с помощью макрокоманд <code>.B</code>, <code>.I</code>, <code>.R</code>. В разделе <code>SYNOPSIS</code> и имя, и флаги выделены жирным шрифтом; прочая информация печатается обычным шрифтом. Разделы <code>ed(1)</code> <code>NAME</code> и <code>SYNOPSIS</code>, например, имеют вид и выводятся как</p>
    <p><code>.SH NAME</code></p>
    <p><code>ed \- text editor</code></p>
    <p><code>.SH SYNOPSIS</code></p>
    <p><code>.В ed</code></p>
    <p><code>[</code></p>
    <p><code>.B \-</code></p>
    <p><code>][</code></p>
    <p><code>.B \-x</code></p>
    <p><code>][ name ]</code></p>
    <empty-line/>
    <p><code>NAME</code></p>
    <p><code> ed - text editor</code></p>
    <p><code>SYNOPSIS</code></p>
    <p><code> ed [ - ][ -x ][ name ]</code></p>
    <p>Заметьте, что используется <code>\-</code>, а не просто <code>-</code>.</p>
    <p>Раздел <code>DESCRIPTION</code> описывает команду и ее флаги. В большинстве случаев это описание команды, а не языка, определяемого командой. Справочник <code>сс(1)</code> не определяет язык Си: он указывает, как запустить команду ее, чтобы компилировать программы на Си, как вызвать оптимизатор, где оставлен результат и т.п. Язык описывается в руководстве для пользователя по Си, на которое есть ссылки в разделе <code>сс(1) SEE ALSO</code>. С другой стороны, разделение не абсолютно: <code>man(7)</code> есть описание языка макрокоманд руководства.</p>
    <p>По соглашению имена команд и метки флагов (такие, как "<code>name</code>" в справочнике <code>ed</code>) печатаются курсивом с помощью макрокоманды <code>.I</code> (первый аргумент печатается курсивом, второй обычным шрифтом). Макрокоманда <code>.IR</code> используется здесь потому, что макрокоманда <code>.I</code> в пакете не обеспечивает недокументированного, но удобного применения второго флага в <code>ms</code>.</p>
    <p>Раздел <code>FILES</code> упоминает любые файлы, неявно используемые командой. <code>DIAGNOSTICS</code> следует включать только в том случае, если команда вырабатывает необычный выходной поток. Это могут быть диагностические сообщения, сведения о состоянии выхода или информация о неожиданных отклонениях от стандартного выполнения команды. Раздел <code>BUGS</code> тоже назван отчасти неверно. Дефекты, о которых здесь сообщается, представляют собой не столько ошибки, сколько недостатки - просто ошибки должны быть исправлены прежде, чем команда будет введена в систему. Чтобы понять, для чего нужны разделы <code>DIAGNOSTICS</code> и <code>BUGS</code>, вам следует пролистать стандартное руководство.</p>
    <p>Поясним на примере, как писать справочник. Источник для <code>hoc(1)</code>, <code>/usr/man/man1/hoc.1</code>, показан на рис. 9.1, а на рис. 9.2 представлен выходной текст после вызова.</p>
    <p><code>$ man -t hoc</code></p>
    <p><code>.TH HOC 1</code></p>
    <p><code>.SH NAME</code></p>
    <p><code>hoc \- диалоговый язык для арифметики с плавающей точкой</code></p>
    <p><code>.SH SYNOPSYS</code></p>
    <p><code>.В hoc</code></p>
    <p><code>[ файл ... ]</code></p>
    <p><code>.SH DESCRIPTION</code></p>
    <p><code>.I Hoc</code></p>
    <p><code>интерпретирует простой язык для арифметики с плавающей</code></p>
    <p><code>точкой, примерно уровня Бейсика, с синтаксисом, подобным</code></p>
    <p><code>Си, и с процедурами и функциями с аргументами, а также</code></p>
    <p><code>с рекурсией.</code></p>
    <p><code>.PP</code></p>
    <p><code>Поименованные</code></p>
    <p><code>.IR файлы</code></p>
    <p><code>читаются и интерпретируются по порядку. Если</code></p>
    <p><code>.I файл</code></p>
    <p><code>не указан или если</code></p>
    <p><code>.I файл это '\-'</code></p>
    <p><code>.I hoc</code></p>
    <p><code>интерпретирует стандартный входной поток.</code></p>
    <p><code>.PP</code></p>
    <p><code>Входной поток</code></p>
    <p><code>.I Hoc состоит из</code></p>
    <p><code>.I выражений и</code></p>
    <p><code>.IR операторов.</code></p>
    <p><code>Выражения вычисляются и их результаты печатаются.</code></p>
    <p><code>Операторы,обычно присваивания и определения функций</code></p>
    <p><code>или процедур, не вырабатывают выходного результата,</code></p>
    <p><code>если они явно не вызывают</code></p>
    <p><code>.IR print.</code></p>
    <p><code>.SH "SEE ALSO"</code></p>
    <p><code>.I</code></p>
    <p><code>Hoc \- Диалоговый язык для арифметики с плавающей точкой</code></p>
    <p><code>Брайана Кернигана и</code></p>
    <p><code>Роба Пайка.</code></p>
    <p><code>.br</code></p>
    <p><code>.IR bas(1),</code></p>
    <p><code>.IR bc(1)</code></p>
    <p><code>and</code></p>
    <p><code>.IR dc(1).</code></p>
    <p><code>.SH BUGS</code></p>
    <p><code>Восстановление после ошибок в определениях функции</code></p>
    <p><code>и процедур несовершенно.</code></p>
    <p><code>.br</code></p>
    <p><code>Обработка концов строк не совсем удобна для пользователя.</code></p>
    <p><emphasis>Рис. 9.1</emphasis>. <code>/usr/man/man1/hoc.1</code></p>
    <cite>
     <subtitle>HOC(1)                                                   HOC(1)</subtitle>
     <subtitle>NAME</subtitle>
     <p>hoc – диалоговый язык для арифметики с плавающей точкой</p>
     <subtitle>SYNOPSYS</subtitle>
     <p><strong>hoc</strong> [ файл … ]</p>
     <subtitle>DESCRIPTION</subtitle>
     <p><emphasis>Hoc</emphasis> интерпретирует простой язык для арифметики с плавающей точкой, примерно уровня Бейсика, с синтаксисом, подобным Си, и с процедурами и функциями с аргументами, а также с рекурсией.</p>
     <p>Поименованные <emphasis>файлы</emphasis> читаются и интерпретируются по порядку. Если <emphasis>файл</emphasis> не указан или если <emphasis>файл </emphasis>— это '-' <emphasis>hoc</emphasis> интерпретирует стандартный входной поток.</p>
     <p>Входной поток Hoc состоит из <emphasis>выражений</emphasis> и <emphasis>операторов</emphasis>. Выражения вычисляются и их результаты печатаются. Операторы, обычно присваивания и определения функций, или процедур, не вырабатывают выходного результата, если они явно не вызывают <emphasis>print</emphasis>.</p>
     <subtitle>"SEE ALSO"</subtitle>
     <p><emphasis>Hoc – Диалоговый язык для арифметики с плавающей точкой Брайана Кернигана и Роба Пайка.</emphasis></p>
     <p><emphasis>bas(1)</emphasis>, <emphasis>bc(1)</emphasis> and <emphasis>dc(1)</emphasis>.</p>
     <subtitle>BUGS</subtitle>
     <p>Восстановление после ошибок в определениях функции и процедур несовершенно. Обработка концов строк не совсем удобна для пользователя.</p>
     <empty-line/>
     <p>8-я версия 1</p>
    </cite>
    <p><emphasis>Рис. 9.2</emphasis>. <code>hoc(1</code>)</p>
    <cite>
     <subtitle>Упражнение 9.8.</subtitle>
     <p>Напишите справочник для <code>doctype</code>. Напишите версию команды man, которая отыскивает документацию по вашим личным программам в вашем собственном каталоге <code>man</code>.</p>
    </cite>
   </section>
   <section>
    <title>
     <p>9.5 Дополнительные средства для подготовки документации</p>
    </title>
    <p>Для подготовки документации существует несколько дополнительных программ. Команда <code>refer(1)</code> отыскивает ссылки на ключевые слова, вставляет эти ссылки в строки вашего документа и помещает раздел ссылок в его конце. Определив соответствующую макрокоманду, вы можете добиться, чтобы <code>refer</code> печатала ссылки в том виде, в каком они вам нужны. Имеются определения для многих журналов по вычислительным наукам. Команда <code>refer</code> является частью седьмой версии, но не включена в некоторые другие версии.</p>
    <p>Программы <code>pic(1)</code> и <code>ideal(1)</code> предназначены для подготовки рисунков, так же как <code>eqn</code> для уравнений. Подготовить рисунки значительно сложнее, чем уравнения (по крайней мере для набора), а поскольку традиции здесь отсутствуют, оба языка отчасти облегчают знакомство с этой техникой и ее использование. В качестве иллюстрации приведем простой рисунок и его выражение на <code>pic</code>.</p>
    <p><code>.PS</code></p>
    <p><code>.ps -1</code></p>
    <p><code>box invis "document"; arrow</code></p>
    <p><code>box dashed "pie"; arrow</code></p>
    <p><code>box dashed "tbl";</code></p>
    <p><code>arrow box dashed "eqn"; arrow</code></p>
    <p><code>box "troff"; arrow</code></p>
    <p><code>box invis "typesetter"</code></p>
    <p><code>[ box invis "macro" "package"</code></p>
    <p><code>spline right then up -&gt; ] with .ne at 2nd last box.s</code></p>
    <p><code>.ps +1</code></p>
    <p><code>.PE</code></p>
    <p><code>          +-----+  +-----+  +-----+  +-------+</code></p>
    <p><code>          |     |  |     |  |     |  |       |</code></p>
    <p><code>документ-&gt;| pic |-&gt;| tbl |-&gt;| eqn |-&gt;| troff |-&gt;наборн. устр-во</code></p>
    <p><code>          |     |  |     |  |     |  |       |</code></p>
    <p><code>          +-----+  +-----+  +-----+  +-------+</code></p>
    <p><code>                                        ^</code></p>
    <p><code>                                        |</code></p>
    <p><code>                пакет макрокоманд ------+</code></p>
    <p>Все рисунки в книге сделаны с помощью <code>pic</code>. Программы <code>pic</code> и <code>ideal</code> не являются частью седьмой версии, но сейчас они в нее включены.</p>
    <p>Программы <code>refer</code>, <code>pic</code> и <code>ideal</code> представляют собой препроцессоры <code>troff</code>. Кроме того, в вашей документации есть программы для просмотра и комментирования текста. Наилучшая из известных программ — <code>spell(1)</code>, которая выдает сообщения о возможных ошибках написания в файлах; мы ее здесь применяли. Программы <code>style(1)</code> и <code>diction(1)</code> анализируют пунктуацию, грамматику и использование языка. Со временем все они были превращены в "Рабочее место писателя" набор программ, помогающих улучшить стиль изложения. Эти программы полезны для обнаружения клише и слов, не являющихся необходимыми, а также некорректных фраз.</p>
    <p>Программа <code>spell</code> считается стандартной. В вашей системе могут быть и другие программы, вы легко обнаружите их с помощью <code>man</code>:</p>
    <p><code>$ man style diction wwb</code></p>
    <p>или путем просмотра <code>/bin</code> и <code>/usr/bin</code>.</p>
    <cite>
     <subtitle>Историческая и библиографическая справка</subtitle>
     <p>Программа <code>troff</code> (ее автор — Дж. Осанна), предназначенная для графических систем CAT-4, имеет свою историю, восходя к <code>RUNOFF</code>, созданной Д. E. Зальтцером для CTSS в MIT в начале 60-х годов. Обе программы имеют общие цели и основной синтаксис команд, хотя <code>troff</code>, конечно, более сложная и мощная программа, а наличие <code>eqn</code> и других препроцессоров значительно повышает ее эффективность. Существует несколько новых программ для наборных устройств с более "цивилизованным" форматом входного текста; наиболее известны из них <code>ТЕХ</code> Д. Кнута ("ТЕХ and Metafont: New Direction in Typesetting", Digital Press, 1979) и <code>Scribe</code> Б. Рейда ("Scribe: a high level approach to computer document formatting". 7th Symposium on the Principles of Programming Languages, 1980).</p>
     <p>Статья Фурута, Дж. Скофилда и А. Шоу "Document Formatting Systems: Survey, Concepts and Issues" (Computing Surveys, 1982) дает хороший обзор таких систем.</p>
     <p>Представляет интерес оригинальная работа по <code>eqn</code> Б. Кернигана и Л. Черри "A system for typesetting mathematics" (CACM, March 1975). Пакеты макрокоманд <code>ms</code>, <code>tbl</code> и <code>refer</code> принадлежат M. Леску; они документированы только в справочном руководстве по UNIX (том 2А).</p>
     <p>Препроцессор <code>pic</code> описан в статье Б. Кернигана "PIC a language for typesetting graphics" (Software Practice and Experience, January, 1982), препроцессор <code>ideal</code> в статье К  Ван Вика "A high level language for describing pictures" (ACM Translation on Graphics, April, 1982).</p>
     <p>Команда <code>spell</code> из файла <code>shell</code>, написанного С. Джонсоном, превратилась в Си-программу Д. МакИлроя. Программа <code>spell</code> из седьмого издания для быстрого поиска использует механизм хеширования и правила для автоматического отделения суффиксов и префиксов, чтобы уменьшить занимаемое словарем место. (См.: McIlroy. М. D. "Development of a spelling list". IEEE Transaction on Communications, January, 1982).</p>
     <p>Программы <code>style</code> и <code>diction</code> описаны в работе Л. Черри "Computer aids for writers" (SYGPLAN Symposium on Text Manipulation, Portland, Oregon, June, 1981).</p>
    </cite>
   </section>
  </section>
  <section>
   <title>
    <p>Глава 10</p>
    <p>Эпилог</p>
   </title>
   <p>Операционной системе UNIX уже более десяти лет, а число использующих ее вычислительных машин растет сейчас быстрее, чем когда-либо. Для системы, сконструированной без коммерческих целей или даже намерений, это уникальный успех.</p>
   <p>Главная причина популярности системы, вероятно, в ее переносимости свойстве, благодаря которому все, кроме небольших частей компиляторов и ядра, выполняется на любом компьютере без изменений. Поэтому разработчикам, запускающим программное обеспечение UNIX на своих машинах, достаточно лишь небольших усилий на ее адаптацию, чтобы успешно пользоваться все расширяющимся рынком программ для UNIX.</p>
   <p>Система UNIX стала популярной задолго до того, как она приобрела коммерческое значение, и даже до того, как начала применяться не только на PDP-11, но и на других машинах. Статья Ритчи и Томпсона в CACM в 1974 г. вызвала интерес в академических кругах, а в 1975 г. шестая версия получила распространение и в университетах. В середине 70-х годов о UNIX заговорили: хотя система не поддерживалась и не имела гарантий, нашлись энтузиасты, которые работали с ней сами и рекомендовали ее другим. Еще одна причина успеха системы заключалась в том, что поколение программистов, использовавших академические системы UNIX, ныне ожидают найти среду UNIX в сфере своей деятельности.</p>
   <p>Чем объясняется такой интерес к системе? Дело в том, что она была сконструирована и построена двумя очень талантливыми людьми, единственная цель которых состояла в создании среды, удобной для разработки программ. Свободные от давления рынка ранние системы были достаточно малы, чтобы в них мог разобраться один человек. Дж. Лайонс излагал шестую версию ядра в рамках курса по операционным системам для выпускников университета Нового Южного Уэльса в Австралии. В своих заметках он писал, что вся документация умещается в "студенческом портфеле" (это зафиксировано в недавних версиях).</p>
   <p>Та ранняя версия базировалась на изобретениях в сфере приложений вычислительной науки, включающих обработку потоков (программные каналы), регулярные выражения, теорию языков (<code>yacc</code>, <code>lex</code> и т.п.) и ряд специальных вопросов типа алгоритма <code>diff</code>. Объединенные в одной системе, они стали ядром с такими возможностями, которыми не всегда располагают даже большие операционные системы. В качестве примера можно привести систему ввода-вывода, состоящую из иерархической файловой системы, редкой по тем временам, устройств, которым поставлены в соответствие имена в файловой системе, так что они не требовали применения специальных утилит, и дюжины основных системных вызовов, подобных примитивам open с двумя аргументами. Программное обеспечение, написанное на языке высокого уровня, поставлялось вместе с системой, так что его можно было изучать и модифицировать. На компьютерном рынке UNIX известна как одна из стандартных операционных систем. Размер ее ядра за последнее десятилетие вырос в 10 раз, хотя, к сожалению, качественно оно улучшается существенно медленнее. Увеличилось число труднопонимаемых программ, которые не созданы в существующей среде. Создаваемые средства обрастают командами с флагами, которые затемняют первоначальный замысел программ. Так как исходные тексты программ зачастую не распространяются вместе с системой, образцы хорошего стиля программирования становятся менее доступными.</p>
   <p>Тем не менее последние версии все еще насыщены идеями,, сделавшими ранние версии столь популярными. Принципы, на которых основана UNIX простота структуры, отсутствие непропорциональных средств, использование существующих программ (вместо того, чтобы создавать их заново), программируемость командного языка, древовидная структура файловой системы и т.д. продолжают доминировать и вытесняют идеи "монолитных" предшествующих систем. Система UNIX, конечно, не может стать навсегда непревзойденной; однако те системы, которые рассчитывают ее "обойти", должны будут заимствовать многие из ее фундаментальных идей.</p>
   <p>В предисловии отмечалось, что существует подход, или философия, UNIX, т.е. особый стиль программирования. Теперь, прочитав всю книгу, вы, должно быть, сможете оценить элементы этого стиля, проиллюстрированного многочисленными примерами.</p>
   <p>Во первых, пусть машина работает. Используйте программы типа <code>grep</code>, <code>wc</code> и <code>awk</code>, чтобы автоматизировать задачи, которые бы вам пришлось делать вручную в других системах.</p>
   <p>Во вторых, пусть работает и человек. Используйте программы, которые уже существуют как "строительные блоки" в ваших программах, с <code>shell</code> и программируемыми фильтрами, чтобы соединить их воедино. Пишите небольшие программы, обеспечивающие взаимодействие с имеющимися, которые решают реальные задачи, как мы сделали это в <code>diff</code>. Среда UNIX богата средствами, пригодными для самых разнообразных комбинаций, выберите из них единственно верную.</p>
   <p>В третьих, выполняйте работу поэтапно. Постройте какую-нибудь простую полезную программу и, исходя из своего опыта, определите, что (если вообще что либо нужно) следует делать далее. Не добавляйте к ней средства, пока вы на практике не выясните, что именно необходимо.</p>
   <p>В-четвертых, создавайте инструментарий. Пишите программы, которые "стыкуются" с существующей средой, усиливая их, а не просто расширяя их возможности. Хорошо построенные, такие программы сами становятся частью чьего либо инструментария.</p>
   <p>Разумеется, система не вполне совершенна. Читая книгу, вы сталкивались со многими условностями, бессмысленными различиями в программах и произвольными ограничениями. Однако, несмотря на все свои недостатки, UNIX, в самом деле, хороша в том, для чего она предназначена: в обеспечении удобной среды программирования. И хотя система уже начала обнаруживать признаки "среднего возраста", она все еще жизнеспособна и все еще популярна. Это заслуга нескольких одаренных человек, которые в 1969 г. набросали на доске проект удобной среды программирования, получившей впоследствии высокую оценку целого поколения программистов.</p>
  </section>
  <section>
   <title>
    <p>Приложение 1</p>
    <p>Краткое описание редактора</p>
   </title>
   <p>Стандартный текстовый редактор UNIX создан К. Томпсоном в начале 70-х годов для вычислительной среды на малых машинах (первая система UNIX ограничивала предельный размер программ пользователя до 8К байт) с терминалами "твердой копии", работавшими при очень низких скоростях (10-15 символов в секунду). Этот редактор написан на базе более ранней версии <code>qed</code>, которая была в то время популярна.</p>
   <p>С развитием технологии <code>ed</code> постигла та же судьба. Почти наверняка вы найдете в своей системе другие редакторы с интересными свойствами, в частности с возможностью "визуального", или "экранного", редактирования, при котором на экране терминала отражаются все вносимые вами коррективы.</p>
   <p>Почему же тогда мы тратим время на, казалось бы, устаревшую программу? Дело в том, что <code>ed</code> выполняет некоторые операции весьма успешно, несмотря на свой возраст. Эта программа есть на всех установках UNIX, и вы всегда найдете ее при переходе с одной системы на другую. Она работает хорошо и с низкоскоростными телефонными линиями, и с любыми терминалами. Кроме того, <code>ed</code> легко запускать из командного файла, в то время как большинство экранных редакторов управляются с терминала и не могут должным образом получить входной поток из файла.</p>
   <p>Редактор <code>ed</code> предоставляет регулярные выражения для поиска по образцу. Регулярные выражения, на которых основан <code>ed</code>, присутствуют во всех частях системы: <code>grep</code> и <code>sed</code> применяют почти такие же, a <code>egrep</code>, <code>awk</code>, <code>lex</code> расширяют их. Shell использует для сравнения имен файлов иной синтаксис, но те же самые идеи. Некоторые экранные редакторы имеют "строчный режим", который предусматривает обращение к регулярным выражениям ed.</p>
   <p>И, наконец, <code>ed</code> обладает высоким быстродействием. Вполне возможно вызвать <code>ed</code>, заменить в файле одну строку, записать новую версию и вернуться из него, причем все это происходит быстрее, чем один только запуск большого и более сложного экранного редактора.</p>
   <subtitle>Основные сведения</subtitle>
   <p>Программа <code>ed</code> редактирует один файл за один раз. Она работает с копией файла. Чтобы внести исправления в первоначальный файл, вы должны дать явную команду. Редактор предоставляет команды для манипуляций с последовательными строками или строками, соответствующими образцу, а также команды для внесения в строки изменений.</p>
   <p>Каждая команда <code>ed</code> представляет собой символ (обычно букву). Большинству команд может предшествовать один или два номера строки, которые указывают, на какую строку или строки должна воздействовать команда: в противном случае подразумевается номер, принятый по умолчанию. Номер строки можно специфицировать абсолютной позицией в файле (1, 2 ...), символами <code>$</code> для последней строки и "<code>.</code>" для текущей, процедурой поиска по образцу, использующей регулярные выражения, и их аддитивными комбинациями.</p>
   <p>Рассмотрим, как с помощью <code>ed</code> можно создавать файлы, используя стихи Де Моргана из первой главы.</p>
   <p><code>$ ed poem</code></p>
   <p><code>? poem </code>Предупреждение: файл poem не существует</p>
   <p><code>а      </code>Начать добавление строк</p>
   <p><code>Great fleas have little fleas</code></p>
   <p><code> upon their backs to bite 'em,</code></p>
   <p><code>And little fleas have lesser fleas,</code></p>
   <p><code> and so ad infinitum.</code></p>
   <p><code>.      </code>Печатаем '.' чтобы закончить ввод</p>
   <p><code>w poem </code>Пишем строки в файл poem</p>
   <p><code>121    </code>ed сообщает, что записан 121 символ</p>
   <p><code>q      </code>Выход</p>
   <p>Команда <code>а</code> добавляет или присоединяет строки. Режим добавления заканчивается строкой, состоящей из одной точки. Из-за отсутствия индикации режима, в котором вы работаете, возможны две распространенные ошибки: ввод текста без команды <code>а</code> и ввод команды до ввода '<code>.</code>'.</p>
   <p>Редактор <code>ed</code> никогда не будет писать ваш текст в файл автоматически; вы должны задать это с помощью команды <code>w</code>. Однако, если вы пытаетесь закончить редактирование без записи ваших изменений, <code>ed</code> выдает '<code>?</code>' как предупреждение. Есть другая команда <code>q</code>, позволяющая завершить работу независимо от внесения исправлений.</p>
   <p><code>$ ed poem</code></p>
   <p><code>121       </code>Файл существует и имеет 121 символ</p>
   <p><code>а         </code>Добавить еще строки в его конец</p>
   <p><code>And the great fleas themselfs, in turn,</code></p>
   <p><code> have greater fleas to go on;</code></p>
   <p><code>While these again have greater still,</code></p>
   <p><code> and greater still, and so on.</code></p>
   <p><code>.         </code>Печатаем '.' для завершения</p>
   <p><code>q         </code>Пытаемся выйти</p>
   <p><code>?         </code>Предупреждение: не было записи</p>
   <p><code>w         </code>Нет имени файла; подразумевается poem</p>
   <p><code>263</code></p>
   <p><code>q         </code>Теперь можно выходить</p>
   <p><code>$ wc poem </code>Проверьте для уверенности</p>
   <p><code>8 46 263 poem</code></p>
   <p><code>$</code></p>
   <subtitle>Временная передача управления <code>shell</code> с помощью '<code>!</code>'</subtitle>
   <p>Если вы запустили <code>ed</code>, то можете временно выйти из него, чтобы запустить другую команду <code>shell</code>. В этом случае нет необходимости прекращать работу достаточно ввести команду <code>ed</code> '<code>!</code>'.</p>
   <p><code>$ ed poem</code></p>
   <p><code>! wc poem </code>Запуск wc без выхода из ed</p>
   <p><code>8 46 263 poem</code></p>
   <p><code>!         </code>Вернулись из команды</p>
   <p><code>q         </code>Выход без w годится: не было исправлений</p>
   <p><code>$</code></p>
   <subtitle>Печать</subtitle>
   <p>Строки файла нумеруются как 1, 2 … Вы можете печатать <code>n</code>-ю строку, дав команду <code>np</code> или просто номер <code>n</code>, и строки с <code>m</code> по <code>n</code>, используя <code>m,np</code>. "Номером строки" <code>$</code> обозначается последняя строка, так что строки можно не считать.</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>1</code></td>
     <td align="left" valign="top">Печатать первую строку; <code>1р</code> то же самое</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>$</code></td>
     <td align="left" valign="top">Печатать последнюю строку; <code>$p</code> то же самое</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>1,$p</code></td>
     <td align="left" valign="top">Печатать строки с первой по последнюю</td>
    </tr>
   </table>
   <p>Печатать файл по одной строке проще всего; нажимая клавишу <code>RETURN</code>, вы можете вернуться на одну строку назад с помощью '<code>-</code>'. Можно комбинировать номера строк с '<code>+</code>' и '<code>-</code>'.</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>$-2,$p</code></td>
     <td align="left" valign="top">Печатать последние три строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>1,2+3p</code></td>
     <td align="left" valign="top">Печатать строки с первой по пятую</td>
    </tr>
   </table>
   <p>Однако нельзя печатать после конца файла или в обратном порядке; команды типа <code>$,$+1p</code> и <code>$,1p</code> считаются незаконными.</p>
   <p>Команда <code>list 1</code> выводит текст в формате с видимыми символами. Это удобно при поиске в файлах управляющих символов, при различении пробелов, табуляции и т.п. (см. <code>vis</code> в гл. 7).</p>
   <subtitle>Образцы</subtitle>
   <p>Как только размер начинает превышать две строки, становится неудобным печатать его весь целиком, чтобы отыскать нужную строку. Редактор <code>ed</code> предлагает способ поиска строк, совпадающих с некоторым образцом, шаблоном: <code>/pattern/</code> обнаруживает очередное вхождение <code>pattern</code>.</p>
   <p><code>$ ed poem 263</code></p>
   <p><code>/flea/ </code>Ищет очередную строку, содержащую flea</p>
   <p><code>Great fleas have little fleas</code></p>
   <p><code>/flea/ </code>Ищет еще одну</p>
   <p><code>And little fleas have lesser fleas,</code></p>
   <p><code>//     </code>Ищет следующую по тому же образцу</p>
   <p><code>And the great fleas themselves, in turn,</code></p>
   <p><code>??     </code>Поиск в обратном направлении по тому же образцу</p>
   <p><code>And little fleas have lesser fleas,</code></p>
   <p>Редактор запоминает образец, применявшийся вами в последний раз, так что можно повторить поиск просто с помощью <code>//</code>. Для поиска в обратном направлении воспользуйтесь <code>?pattern?</code> и <code>??</code>.</p>
   <p>Поиск с помощью <code>/.../</code> и <code>?...?</code> циклический, т.е. продолжается в обратном направлении после достижения одного из концов текста:</p>
   <p><code>$p     </code>Печатать последнюю строку ('p' необязательна)</p>
   <p><code>and greater still, and so on.</code></p>
   <p><code>/flea/ </code>Следующее flea вблизи начала</p>
   <p><code>Great fleas have little fleas</code></p>
   <p><code>??     </code>От начала идет в обратном направлении</p>
   <p><code> have greater fleas to go on;</code></p>
   <p>Результатом поиска по образцу типа <code>/flea/</code> является номер строки, например 1 или <code>$</code>, который может использоваться в том же контексте, что и такие номера:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>1,/flea/p</code></td>
     <td align="left" valign="top">Печатать от единицы до следующего <code>flea</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>?flea?+1,$p</code></td>
     <td align="left" valign="top">Печатать от предыдущего <code>flea + 1</code> до конца</td>
    </tr>
   </table>
   <p>Текущая редактируемая строка. Редактор <code>ed</code> отслеживает последнюю строку, с которой имели дело: печатали или вводили текст, читали из файла. Это текущая строка с именем '<code>.</code>'. Каждая команда определенным образом влияет на текущую строку, обычно настраивая ее на ту, с которой она последний раз работала. Вы можете использовать текущую строку так же, как <code>$</code> или номер строки типа 1:</p>
   <p><code>$ ed poem</code></p>
   <p><code>263</code></p>
   <p><code>.      </code>Печатает текущую строку; после чтения файла</p>
   <p>это то же, что $</p>
   <p><code> and greater still, and so on.</code></p>
   <p><code>.-1,.p </code>Печатает предыдущую строку и еще одну</p>
   <p><code>While these again have greater still,</code></p>
   <p><code> and greater still, and so on.</code></p>
   <p>Выражения для номера строки могут быть сокращены:</p>
   <table>
    <tr align="left">
     <th align="left" valign="top">Сокращение</th>
     <th align="left" valign="top">Эквивалент</th>
     <th align="left" valign="top">Сокращение</th>
     <th align="left" valign="top">Эквивалент</th>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>-1</code></td>
     <td align="left" valign="top"><code>.-1</code></td>
     <td align="left" valign="top"><code>+</code></td>
     <td align="left" valign="top"><code>.+1</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>--</code> или <code>-2</code></td>
     <td align="left" valign="top"><code>.-2</code></td>
     <td align="left" valign="top"><code>++</code> или <code>+2</code></td>
     <td align="left" valign="top"><code>.+2</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>-n</code></td>
     <td align="left" valign="top"><code>.-n</code></td>
     <td align="left" valign="top"><code>+n</code></td>
     <td align="left" valign="top"><code>.+n</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>$-</code></td>
     <td align="left" valign="top"><code>$-1</code></td>
     <td align="left" valign="top"><code>.3</code></td>
     <td align="left" valign="top"><code>.+3</code></td>
    </tr>
   </table>
   <subtitle>Добавление, замена, исключение, вставка</subtitle>
   <p>Команда <code>а</code> (добавить) добавляет строки после определенной строки, команда <code>d</code> (удалить) вычеркивает строки, команда <code>i</code> (вставить) вставляет строки перед определенной строкой, команда <code>с</code> (заменить) заменяет строки, действуя как комбинация команд "удалить" и "вставить".</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>na</code></td>
     <td align="left" valign="top">Добавить текст после строки <code>n</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>ni</code></td>
     <td align="left" valign="top">Вставить текст перед строкой <code>n</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>m,nd</code></td>
     <td align="left" valign="top">Удалить строки с <code>m</code> по <code>n</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>m,nc</code></td>
     <td align="left" valign="top">Заменить строки с <code>m</code> по <code>n</code></td>
    </tr>
   </table>
   <p>Если номера строк не указаны, используется текущая строка. Новый текст для команд <code>а</code>, <code>с</code> и <code>i</code> оканчивается строкой '<code>.</code>'; точка, введенная в последней строке, оставляется. Текущая строка настраивается на следующую строку после последней удаленной, за исключением случая, когда удалена последняя строка, т.е. <code>$</code>.</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>0а</code></td>
     <td align="left" valign="top">Добавить текст в начало (то же, что <code>1i</code>)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>dp</code></td>
     <td align="left" valign="top">Удалить текущую строку, печатать следующую (или последнюю, если <code>$</code>)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.,$dp</code></td>
     <td align="left" valign="top">Удалить отсюда до конца, печатать новую последнюю</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>1,$d</code></td>
     <td align="left" valign="top">Удалить все</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>?pat?,.-1d</code></td>
     <td align="left" valign="top">Удалить от предыдущей, совпадающей с '<code>pat</code>' до той, что перед текущей</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>$dp</code></td>
     <td align="left" valign="top">Удалить последнюю строку, печатать новую последнюю</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>$c</code></td>
     <td align="left" valign="top">Заменить последнюю строку ($а добавляет после последней строки)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>1,$c</code></td>
     <td align="left" valign="top">Заменить все строки</td>
    </tr>
   </table>
   <subtitle>Подстановка, аннулирование</subtitle>
   <p>Нет необходимости перепечатывать целую строку, если в ней нужно заменить лишь несколько символов. Команда подстановки <code>s</code> заменяет одну последовательность символов другой:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>s/old/new/</code></td>
     <td align="left" valign="top">Заменить первую <code>old</code> на <code>new</code> в текущей строке</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>s/old/new/p</code></td>
     <td align="left" valign="top">Заменить первую <code>old</code> на <code>new</code> и печатать строку</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>s/old/new/g</code></td>
     <td align="left" valign="top">Заменить каждую <code>old</code> на <code>new</code> в текущей строке</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>s/old/new/gp</code></td>
     <td align="left" valign="top">Заменить каждую <code>old</code> на <code>new</code> и печатать строку</td>
    </tr>
   </table>
   <p>Заменяется только самое левое вхождение образца в строке, если не написана буква '<code>g</code>'. Команда <code>s</code> выводит измененную строку только в том случае, когда она оканчивается буквой 'p'. Фактически большинство команд <code>ed</code> выполняет свою работу "молча", но почти любая команда может быть завершена буквой <code>p</code> для вывода результата.</p>
   <p>Если подстановкой вы не добились того, что хотели, с помощью команды u (аннулировать) можно уничтожить последнюю подстановку. Текущая строка должна быть настроена на преобразованную строку:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>u</code></td>
     <td align="left" valign="top">Аннулировать последнюю сделанную подстановку</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>up</code></td>
     <td align="left" valign="top">Аннулировать последнюю подстановку и напечатать</td>
    </tr>
   </table>
   <p>Как вам уже известно, командам <code>p</code> и <code>d</code> могут предшествовать один или два номера, указывающие строки, на которые нужно воздействовать. Этот же принцип используется и для команды <code>s</code>.</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>/old/s/old/new/</code></td>
     <td align="left" valign="top">Найти следующую <code>old</code>; заменить на <code>new</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/old/s//new</code></td>
     <td align="left" valign="top">Найти следующую <code>old</code>; заменить на new (образец запоминается)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>1 ,$s/old/new/p</code></td>
     <td align="left" valign="top">Заменить первую <code>old</code> на <code>new</code> в каждой строке; печатать последнюю измененную строку</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>1,$s/old/new/gp</code></td>
     <td align="left" valign="top">Заменить каждую <code>old</code> на <code>new</code> в каждой строке; печатать последнюю измененную строку</td>
    </tr>
   </table>
   <p>Отметим, что <code>1,$s</code> вызывает команду для обработки каждой строки, но это означает лишь самое левое вхождение образца в каждой строке; нужна заключительная команда '<code>g</code>', чтобы заместить все вхождения во всех строках. Кроме того, <code>p</code> выдает только последнюю измененную строку. Для вывода всех измененных строк необходима глобальная команда, которую мы вскоре рассмотрим.</p>
   <p>Символ <code>&amp;</code> означает сокращение; оказавшись где либо справа от команды <code>s</code>, он заменяется образцом из левой части:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>s/big/very &amp;/</code></td>
     <td align="left" valign="top">Заменить <code>big</code> на <code>very big</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>s/big/&amp; &amp;/</code></td>
     <td align="left" valign="top">Заменить <code>big</code> на <code>big big</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>s/.*/(&amp;)/</code></td>
     <td align="left" valign="top">Взять в скобки целую строку (см. <code>.*</code> ниже)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>s/and/\&amp;/</code></td>
     <td align="left" valign="top">Заменить <code>and</code> на <code>&amp;</code> (<code>\</code> отключает специальное значение символа)</td>
    </tr>
   </table>
   <subtitle>Метасимволы и регулярные выражения</subtitle>
   <p>Как и символы <code>*</code>, <code>&gt;</code>, <code>:</code>, имеющие специальный смысл в <code>shell</code>, некоторые символы имеют специальный смысл для <code>ed</code>, если они появляются в образце для поиска или в левой части команды <code>s</code>. Эти символы называют метасимволами, а использующие их образцы регулярными выражениями. В табл. П.1.1 перечислены все символы и их значения. Примеры, приведенные ниже, следует читать в соответствии с таблицей. Специальный смысл любого символа может быть отменен предшествующей ему обратной дробной чертой '<code>\</code>'.</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>с</code></td>
     <td align="left" valign="top">Любой специальный символ задает совпадение с таким же символом</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>\c</code></td>
     <td align="left" valign="top">Отменяет специальный смысл символа <code>с</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>А</code></td>
     <td align="left" valign="top">Соответствует началу строки, когда <code>^</code> начинает образец</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>$</code></td>
     <td align="left" valign="top">Соответствует концу строки, когда <code>$</code> заканчивает образец</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.</code></td>
     <td align="left" valign="top">Совпадает с любым одиночным символом</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>[...]</code></td>
     <td align="left" valign="top">Соответствует одному любому символу в <code>...</code>; допустимы диапазоны типа <code>a-z</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>[^...]</code></td>
     <td align="left" valign="top">Соответствует любому одиночному символу, не входящему в <code>...</code>; допустимы диапазоны</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>r*</code></td>
     <td align="left" valign="top">Соответствует нулевому или более числу вхождений <code>r</code>, где r символ, или <code>[...]</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>&amp;</code></td>
     <td align="left" valign="top">Используется только в правой части s; вставляет фрагмент, совпавший с образом</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>\(...\)</code></td>
     <td align="left" valign="top">Помечает регулярное выражение; найденная строка доступна как <code>\1</code>, и т.д. в левой и правой частях выражения</td>
    </tr>
   </table>
   <p>Таблица П.1.1: <strong>Регулярные выражения редактора</strong></p>
   <empty-line/>
   <p>Символу перевода строки не соответствует ни одно регулярное выражение.</p>
   <table>
    <tr align="left">
     <th align="left" valign="top">Образец</th>
     <th align="left" valign="top">Соответствие</th>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/^$/</code></td>
     <td align="left" valign="top">пустая строка, т.е. только конец строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/./</code></td>
     <td align="left" valign="top">непустая, т.е. по крайней мере один символ</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/^/</code></td>
     <td align="left" valign="top">все строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/thing/</code></td>
     <td align="left" valign="top"><code>thing</code> где либо в строке</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/^thing/</code></td>
     <td align="left" valign="top"><code>thing</code> в начале строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/thing$/</code></td>
     <td align="left" valign="top"><code>thing</code> в конце строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/^thing$/</code></td>
     <td align="left" valign="top">строка, состоящая лишь из <code>thing</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/thing.$/</code></td>
     <td align="left" valign="top"><code>thing</code> плюс любой символ в конце строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/thing\.$/</code></td>
     <td align="left" valign="top"><code>thing.</code> в конце строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/\/thing\//</code></td>
     <td align="left" valign="top"><code>/thing/</code> где либо в строке</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/[tT]hing/</code></td>
     <td align="left" valign="top"><code>thing</code> или <code>Thing</code> где либо в строке</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/thing[0-9]/</code></td>
     <td align="left" valign="top"><code>thing</code>, за которой одна цифра</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/thing[^0-9]/</code></td>
     <td align="left" valign="top"><code>thing</code>, за которой не цифра</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/thing[0-9][^0-9]/</code></td>
     <td align="left" valign="top"><code>thing</code>, за которой цифра и не цифра</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/thing1.*thing2/</code></td>
     <td align="left" valign="top"><code>thing1</code>, затем любая строка, затем <code>thing2</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/^thing1.*thing2$/</code></td>
     <td align="left" valign="top"><code>thing1</code> в начале и <code>thing2</code> в конце</td>
    </tr>
   </table>
   <p>Регулярные выражения, использующие <code>*</code>, выбирают самое левое совпадение с образцом до тех пор, пока это возможно. Отметим, что <code>x*</code> может соответствовать нулю, а <code>xx*</code> одному или более символу.</p>
   <subtitle>Глобальные команды</subtitle>
   <p>Глобальные команды <code>g</code> и <code>v</code> управляют вызовом одной или большего числа других команд, выполняющих преобразования в множестве строк, выбранных регулярным выражением. Команда <code>g</code> наиболее часто используется для печати, подстановки или удаления множества строк:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code><emphasis>m</emphasis>,<emphasis>n</emphasis>g/re/cmd</code></td>
     <td align="left" valign="top">Для всех строк между <emphasis>m</emphasis> и <emphasis>n</emphasis>, которые соответствуют <code>re</code> выполнить cmd</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code><emphasis>m</emphasis>,<emphasis>n</emphasis>v/re/cmd</code></td>
     <td align="left" valign="top">Для всех строк между <emphasis>m</emphasis> и <emphasis>n</emphasis>, которые не соответствуют <code>re</code> выполнить <code>cmd</code></td>
    </tr>
   </table>
   <p>Командам <code>g</code> и <code>v</code> могут предшествовать номера строк, ограничивающие диапазон; по умолчанию принимается диапазон <code>1,$</code>:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>g/.../p</code></td>
     <td align="left" valign="top">Печатать все строки, соответствующие регулярному выражению <code>...</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>g/.../d</code></td>
     <td align="left" valign="top">Убрать все строки соответствующие <code>...</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>g/.../s//repl/p</code></td>
     <td align="left" valign="top">Заменить первое вхождение <code>...</code> в каждой строке на '<code>repl</code>', печатать измененные строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>g/.../s//repl/gp</code></td>
     <td align="left" valign="top">Заменить каждое <code>...</code> на '<code>repl</code>' , печатать измененные строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>g/.../s/pat/repl/</code></td>
     <td align="left" valign="top">В строках, соответствующих <code>...</code>, заменить первую '<code>pat</code>' на '<code>repl</code>'</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>g/.../s/pat/repl/p</code></td>
     <td align="left" valign="top">В строках, соответствующих <code>...</code>, заменить первую '<code>pat</code>' на '<code>repl</code>' и печатать</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>g/.../s/pat/repl/gp</code></td>
     <td align="left" valign="top">В строках, соответствующих <code>...</code>, заменить все '<code>pat</code>' на '<code>repl</code>' и печатать</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>v/.../s/pat/repl/gp</code></td>
     <td align="left" valign="top">В строках, не соответствующих <code>...</code>, заменить все '<code>pat</code>' на '<code>repl</code>' и печатать</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>v/^$/p</code></td>
     <td align="left" valign="top">Печатать все непустые строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>g/.../cmd1\cmd2\cmd3</code></td>
     <td align="left" valign="top">Выполнять составные команды с единственной <code>g</code>, присоединить <code>\</code> к каждой <code>cmd</code> кроме последней</td>
    </tr>
   </table>
   <p>Команды, управляемые командами <code>g</code> или <code>v</code>, также могут использовать номера строк, текущая строка настраивается по очереди на каждую выбранную строку:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>g/thing/.,.+1р</code></td>
     <td align="left" valign="top">Печатать каждую строку с <code>thing</code> и следующую</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>g/^\.EQ/.1, /^\.EN/s/alpha/beta/gp</code></td>
     <td align="left" valign="top">Заменять <code>alpha</code> на <code>beta</code> только между <code>.EQ</code> и <code>.EN</code> и печатать измененные строки</td>
    </tr>
   </table>
   <subtitle>Перемещение и копирование строк</subtitle>
   <p>Команда <code>m</code> перемещает группу смежных строк, а команда <code>t</code> копирует группу строк:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code><emphasis>m</emphasis>,<emphasis>n</emphasis>,md</code></td>
     <td align="left" valign="top">Переместить строки <emphasis>m</emphasis> по <emphasis>n</emphasis> за строку <emphasis>d</emphasis></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code><emphasis>m</emphasis>,<emphasis>n</emphasis>,td</code></td>
     <td align="left" valign="top">Скопировать строки <emphasis>m</emphasis> по <emphasis>n</emphasis> за строку <emphasis>d</emphasis></td>
    </tr>
   </table>
   <p>Если исходные строки не определены, используется текущая строка. Строка назначения <code>d</code> не может быть в диапазоне <code>m,n-1</code>. Ниже приведено несколько общих идиом, включающих <code>m</code> и <code>t</code>.</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>m+</code></td>
     <td align="left" valign="top">Поместить текущую строку после следующей (переставить)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>m-2</code></td>
     <td align="left" valign="top">Поместить текущую строку перед предыдущей</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>m--</code></td>
     <td align="left" valign="top">То же самое: это то же, что <code>-2</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>m-</code></td>
     <td align="left" valign="top">Ничего не делать</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>m$</code></td>
     <td align="left" valign="top">Поместить текущую строку в конец (<code>m0</code> — поместить в начало)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>t.</code></td>
     <td align="left" valign="top">Дублировать текущую строку (<code>t$</code> дублирует в конце)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>-,.t.</code></td>
     <td align="left" valign="top">Дублировать предыдущую и текущую строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>1,$t$</code></td>
     <td align="left" valign="top">Дублировать все множество строк</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>g/^/m0</code></td>
     <td align="left" valign="top">Инвертировать порядок строк</td>
    </tr>
   </table>
   <subtitle>Метки и номера строк</subtitle>
   <p>Команда <code>=</code> печатает номер строки <code>$</code> (слабое умолчание), .<code>=</code> печатает номер текущей строки и т.д. Положение текущей строки не изменяется.</p>
   <p>Команда <code>kc</code> <emphasis>метит</emphasis> нужную строку буквой <code>с</code>; впоследствии на эту строку можно ссылаться с помощью <code>'c</code>. Команда <code>k</code> не меняет положение текущей строки. Метки удобны при перемещении больших фрагментов текста, поскольку они остаются привязанными к строкам, как показано в приведенной ниже последовательности:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>/.../ka</code></td>
     <td align="left" valign="top">Найти строку <code>...</code> и пометить буквой <code>a</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/.../kb</code></td>
     <td align="left" valign="top">Найти строку <code>...</code> и пометить буквой <code>b</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>'a,'bp</code></td>
     <td align="left" valign="top">Печатать целый диапазон, чтобы быть уверенным</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/.../</code></td>
     <td align="left" valign="top">Найти нужную строку</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>'а,'bm</code></td>
     <td align="left" valign="top">Поместить выбранные строки после нее</td>
    </tr>
   </table>
   <subtitle>Объединение, расщепление и реорганизация строк</subtitle>
   <p>Строки могут быть объединены с помощью команды <code>j</code> (пробелы не добавляются):</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code><emphasis>m</emphasis>,<emphasis>n</emphasis>j</code></td>
     <td align="left" valign="top">объединяет строки с <code>m</code> по <code>n</code> в одну</td>
    </tr>
   </table>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>jp</code></td>
     <td align="left" valign="top">Объединить текущую строку со следующей и печатать</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>-,.jp</code></td>
     <td align="left" valign="top">Объединить предыдущую строку с текущей и печатать</td>
    </tr>
   </table>
   <p>По умолчанию принимается диапазон <code>.,.+1</code>;</p>
   <p>Строки можно расщепить командой подстановки, отделив новую строку:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>s/part1part2/part1\part2/</code></td>
     <td align="left" valign="top">Расщепить строку на две части</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>s/ /\ /g</code></td>
     <td align="left" valign="top">Расщепить по каждому пробелу. Оставить одно слово на строку</td>
    </tr>
   </table>
   <p>Текущей становится последняя созданная строка.</p>
   <p>Чтобы манипулировать не только целыми фрагментами, выбираемыми регулярными выражениями, но и их соответствующими частями, используйте <emphasis>помеченные регулярные выражения</emphasis>: если конструкция <code>\(...\)</code> появляется в регулярном выражении, то часть соответствующего ей фрагмента доступна как <code>\1</code>. Возможно до девяти помеченных выражений, на которые ссылаются с помощью <code>\1</code>, <code>\2</code> и т.д.</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>s/\(...\)\(.*\)/\2\1/</code></td>
     <td align="left" valign="top">Поместить 3 первых символа в конец</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/\(..*\)\1/</code></td>
     <td align="left" valign="top">Найти строки, содержащие повторяющиеся смежные цепочки символов</td>
    </tr>
   </table>
   <subtitle>Команды, работающие с файлами</subtitle>
   <p>Командам <code>r</code> и <code>w</code> (читать и писать) могут предшествовать номера строк:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code><emphasis>n</emphasis>r file</code></td>
     <td align="left" valign="top">Читать <code>file</code>; добавить его после строки <emphasis>n</emphasis>; текущей становится последняя прочитанная строка</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code><emphasis>m</emphasis>,<emphasis>n</emphasis>w file</code></td>
     <td align="left" valign="top">Писать строки <emphasis>m-n</emphasis> в <code>file</code>; положение текущей строки не изменяется</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code><emphasis>m</emphasis>,<emphasis>n</emphasis>w file</code></td>
     <td align="left" valign="top">Добавить строки <emphasis>m-n</emphasis> к <code>file</code>; положение текущей строки не изменяется</td>
    </tr>
   </table>
   <p>По умолчанию диапазон для <code>w</code> и <code>W</code> (команда <code>W</code> приведена ниже в табл. П.1.2) — это целый файл. Значение <emphasis>n</emphasis> по умолчанию для <code>r</code> равно <code>$,</code> что представляется не очень удачным. Будьте внимательны.</p>
   <p>Редактор <code>ed</code> запоминает первое использованное имя файла из командной строки или из команд <code>r</code>, <code>w</code>. Команда <code>f</code> (файл) печатает или заменяет имя запомненного файла:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>f</code></td>
     <td align="left" valign="top">Печатать имя запомненного файла</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>f file</code></td>
     <td align="left" valign="top">Установить запомненное имя на '<code>file</code>'</td>
    </tr>
   </table>
   <p>Команда <code>e</code> (редактировать) вновь вызывает ed с запомненным или новым файлом:</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>e</code></td>
     <td align="left" valign="top">Начать редактировать запомненный файл</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>e file</code></td>
     <td align="left" valign="top">Начать редактировать '<code>file</code>'</td>
    </tr>
   </table>
   <p>Команда <code>е</code> защищена тем же способом, что и <code>q</code>: если вы не записали измененную версию, первая команда <code>е</code> выдает сообщение об ошибке; <code>е</code> вновь инициализирует редактор независимо от внесения изменений. В некоторых системах <code>ed</code> связан с <code>е</code>, так что одна и та же команда (<code>е filename</code>) может использоваться внутри и вне редактора.</p>
   <subtitle>Шифрование</subtitle>
   <p>Файлы могут быть зашифрованы по записи и дешифрованы при чтении с помощью команды <code>x</code>; пароль будет запрошен. Шифрование происходит тем же способом, что и в <code>crypt(1)</code>. В некоторых системах команда <code>x</code> заменена на <code>X</code> (прописную букву) во избежание случайностей.</p>
   <subtitle>Сводка команд</subtitle>
   <p>В табл. П.1.2 перечислены команды редактора, а в табл. П.1.3 допустимые номера строк. Каждой команде предшествует нуль, один или два номера строк, указывающие число используемых строк, если их нет, принимается соглашение по умолчанию. За большинством команд может следовать буква p для вывода последней обработанной строки или 1 для формата списка. Текущей обычно становится последняя обработанная строка; настройка не меняется командами <code>f</code>, <code>k</code>, <code>w</code>, <code>x</code>, <code>=</code>, <code>!</code>.</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>.а</code></td>
     <td align="left" valign="top">Выполнять ввод до тех пор, пока не напечатана строка, содержащая только '<code>.</code>'</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.,.с</code></td>
     <td align="left" valign="top">Заменить строки, новый текст заканчивается так же, как для команды <code>а</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.,.d</code></td>
     <td align="left" valign="top">Исключить строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>е file</code></td>
     <td align="left" valign="top">Вновь начать редактировать <code>file</code>. Редактирование начинается даже в том случае, если исправления не записаны</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>f file</code></td>
     <td align="left" valign="top">Запомнить имя файла как <code>file</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>1,$g/re/cmds</code></td>
     <td align="left" valign="top">Выполнить <code>cmds</code> для каждой строки, соответствующей регулярному выражению re; отдельные команды в <code>cmds</code> разделены <code>\newline</code> (<code>\</code>+"перевод строки")</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.i</code></td>
     <td align="left" valign="top">Вставить текст перед строкой; он заканчивается так же, как для команды <code>a</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.,.+1j</code></td>
     <td align="left" valign="top">.Соединить строки в одну</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.kc</code></td>
     <td align="left" valign="top">Пометить строку буквой <code>с</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>...l</code></td>
     <td align="left" valign="top">Перечислить строки, делая невидимые символы видимыми</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.,.m line</code></td>
     <td align="left" valign="top">Переместить строки после строки <code>line</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.,.p</code></td>
     <td align="left" valign="top">Печатать строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>q</code></td>
     <td align="left" valign="top">Выйти. <code>Q</code> выходит, даже если исправления не записаны</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>$r file</code></td>
     <td align="left" valign="top">Читать <code>file</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.,.s/re/new/</code></td>
     <td align="left" valign="top">Заменить <code>new</code> на то, что соответствует re</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.,.t line</code></td>
     <td align="left" valign="top">Скопировать строки после <code>line</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.u</code></td>
     <td align="left" valign="top">Аннулировать последнюю подстановку в строке (только одну)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>1,$v/re/cmds</code></td>
     <td align="left" valign="top">Выполнить команды <code>ed cmds</code> для каждой строки, не соответствующей <code>re</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>1,$w file</code></td>
     <td align="left" valign="top">Записать строки в файл; <code>W</code> добавляет (строки к файлу) вместо того чтобы записывать (как новый файл)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>X</code></td>
     <td align="left" valign="top">Войти в режим шифрования (или <code>ed -х</code> имя_файла)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>$=</code></td>
     <td align="left" valign="top">Печатать номер строки</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>! cmdline</code></td>
     <td align="left" valign="top">Выполнить команду UNIX <code>cmdline</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>(.+1) newline</code></td>
     <td align="left" valign="top">Печатать строку</td>
    </tr>
   </table>
   <p>Таблица П.1.2: <strong>Сводка команд</strong> <code><strong>ed</strong></code></p>
   <empty-line/>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>n</code></td>
     <td align="left" valign="top">Абсолютный номер строки n, n = 0,1, 2,...</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>.</code></td>
     <td align="left" valign="top">Текущая строка</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>$</code></td>
     <td align="left" valign="top">Последняя строка текста</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>/re/</code></td>
     <td align="left" valign="top">Следующая строка, соответствующая <code>re</code>; после последней <code>$</code> циклическое движение к первой строке</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>?re?</code></td>
     <td align="left" valign="top">Предыдущая строка, соответствующая re; после первой циклическое движение к последней $</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>'c</code></td>
     <td align="left" valign="top">Строка с меткой <code>с</code></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>N1+/-n</code></td>
     <td align="left" valign="top">Строка N1+/-n (аддитивная комбинация)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>N1,N2</code></td>
     <td align="left" valign="top">Строки с N1 по N2</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>N1;N2</code></td>
     <td align="left" valign="top">Команда: сделать строку N1 текущей, затем вычислить N2. N1 и N2 могут быть определены любым из перечисленных выше способов</td>
    </tr>
   </table>
   <p>Таблица П.1.3: <strong>Номера строк в</strong> <code><strong>ed</strong></code></p>
   <empty-line/>
   <cite>
    <subtitle>Упражнение</subtitle>
    <p>Если вы думаете, что знаете <code>ed</code>, попробуйте выполнить текст (см. справочное руководство по <code>quiz(6)</code>).</p>
   </cite>
  </section>
  <section>
   <title>
    <p>Приложение 2</p>
    <p>Справочное руководство по <code>hoc</code></p>
   </title>
   <epigraph>
    <p>Hoc — диалоговый язык для арифметики с плавающей точкой</p>
    <text-author>Б. Керниган</text-author>
    <text-author>Р. Пайк</text-author>
   </epigraph>
   <annotation>
    <subtitle>РЕЗЮМЕ</subtitle>
    <p><emphasis>Hoc</emphasis> — это простой программируемый интерпретатор для выражений с плавающей точкой. Он обеспечивает поток управления в стиле Си, определения функций и обычные числовые встроенные функции, такие, как косинус и логарифм.</p>
   </annotation>
   <subtitle>Выражения</subtitle>
   <p>Hoc представляет язык выражений, во многом подобный Си: хотя он и содержит несколько управляющих операторов, большинство операторов, например присваивания, суть выражения, значения которых не принимаются во внимание. Так, оператор присваивания <code>=</code> присваивает значение своей правой части левому операнду и вырабатывает значение, используемое в многократном присваивании. Грамматика выражений имеет вид:</p>
   <p><code>выражение : число</code></p>
   <p><code> | переменная</code></p>
   <p><code> | (выражение)</code></p>
   <p><code> | выражение бинарная_операция выражение</code></p>
   <p><code> | унарная_операция выражение</code></p>
   <p><code> | функция(аргументы)</code></p>
   <p>Числа представляются с плавающей точкой. Формат ввода распознается с помощью <code>scanf(3)</code>: цифры, десятичная точка, цифры, <code>е</code> или <code>E</code>, показатель степени со знаком. Должна присутствовать по крайней мере одна цифра или десятичная точка; другие компоненты являются необязательными.</p>
   <p>Имена переменных формируются из букв, за которыми следуют строки букв и цифр. Здесь <emphasis>бинарная_операция</emphasis> означает двуместные операции, такие, как сложение или логическое сравнение, а <emphasis>унарная_операция</emphasis> — две операции отрицания: '<code>!</code>' (логическое отрицание НЕ) и (арифметическое отрицание, перемена знака). Все операции перечислены в табл. П.2.1.</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>^</code></td>
     <td align="left" valign="top">Возведение в степень (FORTRAN <code>**</code>), правоассоциативна</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>! -</code></td>
     <td align="left" valign="top">Одноместные логическое и арифметическое отрицания</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>* /</code></td>
     <td align="left" valign="top">Умножение, деление</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>+ -</code></td>
     <td align="left" valign="top">Сложение, вычитание</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>&gt; &gt;=</code></td>
     <td align="left" valign="top">Операции отношения: больше, больше или равно</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>&lt; &lt;=</code></td>
     <td align="left" valign="top">Меньше, меньше или равно</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>== !=</code></td>
     <td align="left" valign="top">Равно, не равно (все отношения одинакового приоритета)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>&amp;&amp;</code></td>
     <td align="left" valign="top">Логическое И (оба операнда всегда вычисляются)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>||</code></td>
     <td align="left" valign="top">Логическое ИЛИ (оба операнда всегда вычисляются)</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>=</code></td>
     <td align="left" valign="top">Присваивание, правоассоциативна</td>
    </tr>
   </table>
   <p>Таблица П.2.1. <strong>Операции по порядку уменьшения приоритета</strong></p>
   <empty-line/>
   <p>Функции, как описывается ниже, могут быть определены пользователем. Аргументы функций — это выражения, разделяемые запятыми. В табл. П.2.2 перечислено несколько встроенных функций, имеющих по одному аргументу.</p>
   <table>
    <tr align="left">
     <td align="left" valign="top"><code>abs(x)</code></td>
     <td align="left" valign="top">Абсолютная величина <emphasis>x</emphasis></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>atan(x)</code></td>
     <td align="left" valign="top">Арктангенс <emphasis>x</emphasis></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>cos(x)</code></td>
     <td align="left" valign="top">Косинус <emphasis>x</emphasis></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>exp(х)</code></td>
     <td align="left" valign="top">Экспонента <emphasis>x</emphasis></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>int(x)</code></td>
     <td align="left" valign="top">Целая часть <emphasis>x</emphasis>, усеченная в сторону нуля</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>log(x)</code></td>
     <td align="left" valign="top">Натуральный логарифм <emphasis>x</emphasis></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>log10(x)</code></td>
     <td align="left" valign="top">Десятичный логарифм <emphasis>x</emphasis></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>sin(x)</code></td>
     <td align="left" valign="top">Синус <emphasis>x</emphasis></td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>sqrt(x)</code></td>
     <td align="left" valign="top">Корень квадратный из <emphasis>x</emphasis></td>
    </tr>
   </table>
   <p>Таблица П.2.2. <strong>Встроенные функции</strong></p>
   <empty-line/>
   <p>Логические выражения имеют значения 1 (истина) и 0 (ложь). Как и в Си, любое ненулевое значение означает истину. При всех операциях над числами с плавающей точкой сравнения на равенство могут быть неточными. Кроме того, <code>hoc</code> имеет несколько встроенных констант, приведенных в табл. П.2.3.</p>
   <table>
    <tr align="left">
     <td align="left" valign="top">DEG</td>
     <td align="left" valign="top">57.2957795130823208768</td>
     <td align="left" valign="top">180/&#960;, градусы на радианы</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>E</code></td>
     <td align="left" valign="top">2.71828182845904523536</td>
     <td align="left" valign="top">е, основание натуральных логарифмов</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>GAMMA</code></td>
     <td align="left" valign="top">0.57721566490153286060</td>
     <td align="left" valign="top">&#947;, константа Эйлера-Масчерони</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>PHI</code></td>
     <td align="left" valign="top">1.61803398874989484820</td>
     <td align="left" valign="top">(&#8730;5+1)/2, золотое сечение</td>
    </tr>
    <tr align="left">
     <td align="left" valign="top"><code>PI</code></td>
     <td align="left" valign="top">3.14159265358979323846</td>
     <td align="left" valign="top">&#960;, круговое трансцендентное число</td>
    </tr>
   </table>
   <p>Таблица П.2.3. <strong>Встроенные константы</strong></p>
   <empty-line/>
   <subtitle>Операторы и поток управления</subtitle>
   <p>Операторы <code>hoc</code> имеют следующую грамматику:</p>
   <p><code>оператор : выражение</code></p>
   <p><code> | переменная выражение</code></p>
   <p><code> | процедура (список, аргументов)</code></p>
   <p><code> | while (выражение) оператор</code></p>
   <p><code> | if (выражение) оператор</code></p>
   <p><code> | if (выражение) оператор else оператор</code></p>
   <p><code> | список_операторов</code></p>
   <p><code> | print список_выражений</code></p>
   <p><code> | return возможное_выражение</code></p>
   <p><code>список_операторов : (пусто)</code></p>
   <p><code> | список_операторов оператор</code></p>
   <p>Присваивание распознается по умолчанию как оператор, а не как выражение, поэтому после ввода в диалоге присваиваний их значения не печатаются.</p>
   <p>Отметим, что символ <code>;</code> не является для <code>hoc</code> специальным: оператор оканчивается символом перевода строки. Это обусловливает некоторые особенности. Ниже показан допустимый оператор <code>if</code>:</p>
   <p><code>if (x &lt; 0) print(у) else print (z)</code></p>
   <p><code>if (x &lt; 0) {</code></p>
   <p><code> print(y)</code></p>
   <p><code>} else {</code></p>
   <p><code> print(z)</code></p>
   <p><code>}</code></p>
   <p>Во втором примере скобки не обязательны: символ перевода строки после <code>if</code> оканчивал бы оператор и вызывал бы синтаксическую ошибку там, где опущены скобки.</p>
   <p>Синтаксис и семантика средств управления в <code>hoc</code> в основном те же, что и в Си. Одинаковы также <code>while</code> и <code>if</code>, однако в <code>hoc</code> нет операторов <code>break</code> и <code>continue</code>.</p>
   <subtitle>Ввод и вывод: <code>read</code> и <code>print</code></subtitle>
   <p>Функция ввода <code>read</code> (читать) имеет, подобно другим встроенным функциям, один аргумент: однако он не является выражением: это имя переменной. Следующее число, как определено выше, читается из стандартного входного потока и присваивается поименованной переменной. Функция <code>read</code> возвращает значения 1 (истина), если величина была прочитана, и 0 (ложь), если <code>read</code> встретила конец файла либо ошибку.</p>
   <p>Выходной поток порождается оператором <code>print</code>. Аргументы <code>print</code> составляют разделяемый запятыми список выражений и строк, взятых в кавычки, как в Си. Символы перевода строки должны добавляться: <code>print</code> их никогда автоматически не вводит.</p>
   <p>Отметим, что <code>read</code> есть специальная встроенная функция и поэтому получает один аргумент в скобках, тогда как <code>print</code> оператор, получающий список, разделяемый запятыми без скобок:</p>
   <p><code>while (read (x)) {</code></p>
   <p><code> print "value is", x, "\n"</code></p>
   <p><code>}</code></p>
   <subtitle>Функции и процедуры</subtitle>
   <p>Функции и процедуры в <code>hoc</code> различаются, хотя и определены одним и тем же механизмом. Это различие введено просто для контроля ошибок во время исполнения: возврат значения является ошибкой для процедуры, для функции же ошибочно не возвращать значения.</p>
   <p>Синтаксис определения таков:</p>
   <p><code>function: func имя () оператор</code></p>
   <p><code>procedure: proc имя() оператор</code></p>
   <p>Здесь <emphasis>имя</emphasis> может быть именем некоторой переменной встроенные функции исключаются. Определение, вплоть до открывающейся скобки оператора, должно помещаться на одной строке, как в приведенном выше операторе <code>if</code>.</p>
   <p>В отличие от Си тело функции или процедуры может быть любым оператором, не обязательно составным (в скобках). Поскольку символ ; не имеет своего значения в <code>hoc</code>, пустое тело процедуры формируется пустой парой скобок.</p>
   <p>Функции и процедуры при вызовах могут получать аргументы, отделенные запятыми. На аргументы ссылаются так же, как в <code>shell</code>: <code>$3</code> относится к третьему, индексируемому, начиная с единицы, аргументу. Они передаются значениями и внутри функций семантически эквивалентны переменным. Ссылка на аргумент с помощью числа, превышающего число аргументов, переданных процедуре, считается ошибкой. Контроль ошибок динамический, поскольку подпрограмма может иметь переменное число параметров, если ее начальные аргументы влияют на это число (см. функцию <code>printf</code> в Си).</p>
   <p>Функции и процедуры могут быть рекурсивными, но стек имеет ограниченную глубину (около сотни вызовов).</p>
   <subtitle>Примеры</subtitle>
   <p>• Ниже показано определение функции Аккерманна в <code>hoc</code>:</p>
   <p><code>$ hoc</code></p>
   <p><code>time ack() {</code></p>
   <p><code> if ($1 == 0) return $2+1</code></p>
   <p><code> if ($2 ==0) return ack($1 - 1, 1)</code></p>
   <p><code> return ack($1 - 1, ack($1, $2 - 1))</code></p>
   <p><code>}</code></p>
   <p><code>ack(3,2)</code></p>
   <p><code> 29</code></p>
   <p><code>ack(3,3)</code></p>
   <p><code> 61</code></p>
   <p><code>ack(3,4)</code></p>
   <p><code> hoc: стек слишком велик (строка 8)</code></p>
   <p><code>...</code></p>
   <p>• Формула Стирлинга:</p>
   <image l:href="#img_11.png"/>
   <p><code>$ hoc</code></p>
   <p><code>func stirl() {</code></p>
   <p><code> return sqrt(2 * $1 * PI) * ($1 / E) ^ $1 * (1 + 1 / (12 * $1))</code></p>
   <p><code>}</code></p>
   <p><code>stirl(10) 3628684.7</code></p>
   <p><code>stirl(20) 2.4328818e+18</code></p>
   <p>• Функция факториал <emphasis>n</emphasis>!</p>
   <p><code>func fac() if ($1 &lt;= 0) return 1 else return $1 * fac($1 - 1)</code></p>
   <p>Отношение факториала к приближению Стирлинга:</p>
   <p><code>i = 9</code></p>
   <p><code>while ((i = i + 1) &lt;= 20) {</code></p>
   <p><code> print i, " ", fac(i)/stirl(i), "\n"</code></p>
   <p><code>}</code></p>
   <p><code>10 1.0000318</code></p>
   <p><code>11 1.0000265</code></p>
   <p><code>12 1.0000224</code></p>
   <p><code>13 1.0000192</code></p>
   <p><code>14 1.0000166</code></p>
   <p><code>15 1.0000146</code></p>
   <p><code>16 1.0000128</code></p>
   <p><code>17 1.0000114</code></p>
   <p><code>18 1.0000102</code></p>
   <p><code>19 1.0000092</code></p>
   <p><code>20 1.0000083</code></p>
  </section>
  <section>
   <title>
    <p>Приложение 3</p>
    <p>Исходные тексты калькулятора <code>hoc</code></p>
   </title>
   <section>
    <p>These files contain all the code from "The Unix Programming Environment", by Brian Kernighan and Rob Pike (Prentice Hall, 1984, ISBN 0-13-937681-X). A separate hoc6 distribution contains any fixes that we have applied to that; the version in this file is from the book.</p>
    <p>Copyright © Lucent Technologies, 1997. All Rights Reserved</p>
    <p>Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both the copyright notice and this permission notice and warranty disclaimer appear in supporting documentation, and that the name of Lucent Technologies or any of its entities not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.</p>
    <p>LUCENT TECHNOLOGIES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.</p>
   </section>
   <section>
    <title>
     <p>3.1 <code>hoc1</code></p>
    </title>
    <section>
     <title>
      <p>3.1.1 <code>makefile</code></p>
     </title>
     <p><code>hoc1: hoc.o</code></p>
     <p><code>      cc hoc.o -o hoc1</code></p>
    </section>
    <section>
     <title>
      <p>3.1.2 <code>hoc.y</code></p>
     </title>
     <p><code>%{</code></p>
     <p><code>#define YYSTYPE double /* data type of yacc stack */</code></p>
     <p><code>%}</code></p>
     <p><code>%token NUMBER</code></p>
     <p><code>%left '+' /* left associative, same precedence */</code></p>
     <p><code>%left '*' '/' /* left assoc., higher precedence */</code></p>
     <p><code>%%</code></p>
     <p><code>list : /* nothing */</code></p>
     <p><code> | list '\n'</code></p>
     <p><code> | list expr '\n' { printf("\t%.8g\n", $2); }</code></p>
     <p><code> ;</code></p>
     <p><code>expr: NUMBER { $$ = $1; }</code></p>
     <p><code> | expr '+' expr { $$ = $1 + $3; }</code></p>
     <p><code> | expr '-' expr { $$ = $1 - $3; }</code></p>
     <p><code> | expr '*' expr { $$ = $1 * $3; }</code></p>
     <p><code> | expr '/' expr { $$ = $1 / $3; }</code></p>
     <p><code> | '(' expr ')' { $$ = $2; }</code></p>
     <p><code> ;</code></p>
     <p><code>%%</code></p>
     <p><code>/* end of grammar */</code></p>
     <empty-line/>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;ctype.h&gt;</code></p>
     <p><code>char *progname; /* for error messages */</code></p>
     <p><code>int lineno = 1;</code></p>
     <empty-line/>
     <p><code>main(argc, argv) /* hoc1 */</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> progname = argv[0];</code></p>
     <p><code> yyparse();</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>yylex() /* hoc1 */</code></p>
     <p><code>{</code></p>
     <p><code> int c;</code></p>
     <empty-line/>
     <p><code> while ((c=getchar()) == ' ' || c == '\t')</code></p>
     <p><code>  ;</code></p>
     <p><code> if (c == EOF)</code></p>
     <p><code>  return 0;</code></p>
     <p><code> if (c == '.' || isdigit(c)) { /* number */</code></p>
     <p><code>  ungetc(c, stdin);</code></p>
     <p><code> scanf("%lf", &amp;yylval);</code></p>
     <p><code> return NUMBER;</code></p>
     <p><code> if (c == '\n')</code></p>
     <p><code>  lineno++;</code></p>
     <p><code> return c;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>yyerror(s) /* called for yacc syntax error */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, (char*)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>warning(s, t) /* print warning message */</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> fprintf(stderr, "%s: %s", progname, s);</code></p>
     <p><code> if (t)</code></p>
     <p><code>  fprintf(stderr, " %s", t);</code></p>
     <p><code> fprintf(stderr, " near line %d\n", lineno);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.1.3 <code>hoc1.y</code>, версия 1.5</p>
     </title>
     <p><code>%{</code></p>
     <p><code>#define YYSTYPE double /* data type of yacc stack */</code></p>
     <p><code>%}</code></p>
     <p><code>%token NUMBER</code></p>
     <p><code>%left '-' '+'</code></p>
     <p><code>%left '*' '/'</code></p>
     <p><code>%left UNARYMINUS</code></p>
     <p><code>%%</code></p>
     <empty-line/>
     <p><code>list: /* nothing */</code></p>
     <p><code> | list '\n'</code></p>
     <p><code> | list expr '\n' { printf("\t%.8g\n", $2); }</code></p>
     <p><code> ;</code></p>
     <p><code>expr: NUMBER { $$ = $1; }</code></p>
     <p><code> | expr '+' expr { $$ = $1 + $3; }</code></p>
     <p><code> | expr '-' expr { $$ = $1 - $3; }</code></p>
     <p><code> | expr '*' expr { $$ = $1 * $3; }</code></p>
     <p><code> | expr '/' expr { $$ = $1 / $3; }</code></p>
     <p><code> | '-' expr %prec UNARYMINUS { $$ = -$2; } /* new */</code></p>
     <p><code> | '(' expr ')' { $$ = $2; }</code></p>
     <p><code> ;</code></p>
     <p><code>%%</code></p>
     <p><code>/* end of grammar */</code></p>
     <empty-line/>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;ctype.h&gt;</code></p>
     <p><code>char *progname; /* for error messages */</code></p>
     <p><code>int lineno = 1;</code></p>
     <empty-line/>
     <p><code>main(argc, argv) /* hoc1 */</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> progname = argv[0];</code></p>
     <p><code> yyparse();</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>yylex() /* hoc1 */</code></p>
     <p><code>{</code></p>
     <p><code> int c;</code></p>
     <empty-line/>
     <p><code> while ((c=getchar()) == ' ' || с == '\t')</code></p>
     <p><code>  ;</code></p>
     <p><code> if (c == EOF)</code></p>
     <p><code>  return 0;</code></p>
     <p><code> if (c == '.' || isdigit(c)) { /* number */</code></p>
     <p><code>  ungetc(c, stdin);</code></p>
     <p><code>  scanf("%lf", &amp;yylval);</code></p>
     <p><code>  return NUMBER;</code></p>
     <p><code> }</code></p>
     <p><code> if (c == '\n')</code></p>
     <p><code>  lineno++;</code></p>
     <p><code> return c;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>yyerror(s)</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, (char *)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>warning(s, t)</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> fprintf(stderr, "%s: %s", progname, s);</code></p>
     <p><code> if (t &amp;&amp; *t)</code></p>
     <p><code>  fprintf(stderr, "%s", t);</code></p>
     <p><code> fprintf(stderr, " near line %d\n", lineno);</code></p>
     <p><code>}</code></p>
    </section>
   </section>
   <section>
    <title>
     <p>3.2 <code>hoc2</code></p>
    </title>
    <section>
     <title>
      <p>3.2.1 <code>hoc.y</code></p>
     </title>
     <p><code>%{</code></p>
     <p><code>double mem[26]; /* memory for variables 'a'..'z' */</code></p>
     <p><code>%}</code></p>
     <p><code>%union { /* stack type */</code></p>
     <p><code> double val; /* actual value */</code></p>
     <p><code> int index; /* index into mem[] */</code></p>
     <p><code>}</code></p>
     <p><code>%token &lt;val&gt; NUMBER</code></p>
     <p><code>%token &lt;index&gt; VAR</code></p>
     <p><code>%type &lt;val&gt; expr</code></p>
     <p><code>%right '='</code></p>
     <p><code>%left '+' '-'</code></p>
     <p><code>%left '*' '/'</code></p>
     <p><code>%left UNARYMINUS</code></p>
     <p><code>%%</code></p>
     <p><code>list: /* nothing */</code></p>
     <p><code> | list '\n'</code></p>
     <p><code> | list expr '\n' { printf("\t%.8g\n\ $2); }</code></p>
     <p><code> | list error '\n' { yyerrok; }</code></p>
     <p><code> ;</code></p>
     <p><code>expr: NUMBER</code></p>
     <p><code> | VAR { $$ = mem[$1]; }</code></p>
     <p><code> | VAR '=' expr { $$ = mem[$1] = $3; }</code></p>
     <p><code> | expr '+' expr { $$ = $1 + $3; }</code></p>
     <p><code> | expr '-' expr { $$ = $1 - $3; }</code></p>
     <p><code> | expr '*' expr { $$ = $1 * $3; }</code></p>
     <p><code> | expr '/' expr {</code></p>
     <p><code>  if ($3 == 0.0)</code></p>
     <p><code>  execerror("division by zero", "");</code></p>
     <p><code>  $$ = $1 / $3;</code></p>
     <p><code> }</code></p>
     <p><code> | '(' expr ')' { $$ = $2; }</code></p>
     <p><code> | '-' expr %prec UNARYMINUS { $$ = -$2; }</code></p>
     <p><code> ;</code></p>
     <p><code>%%</code></p>
     <p><code>/* end of grammar */</code></p>
     <empty-line/>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;ctype.h&gt;</code></p>
     <p><code>char *progname;</code></p>
     <p><code>int lineno = 1;</code></p>
     <p><code>#include &lt;signal.h&gt;</code></p>
     <p><code>#include &lt;setjmp.h&gt;</code></p>
     <p><code>jmp_buf begin;</code></p>
     <empty-line/>
     <p><code>main(argc, argv) /* hoc2 */</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> int fpecatch();</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> setjmp(begin);</code></p>
     <p><code> signal(SIGFPE, fpecatch);</code></p>
     <p><code> yyparse();</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>yylex() /* hoc2 */</code></p>
     <p><code>{</code></p>
     <p><code> int c;</code></p>
     <empty-line/>
     <p><code> while ((c=getchar()) == ' ' || c == '\t')</code></p>
     <p><code>  ;</code></p>
     <p><code> if (c == EOF)</code></p>
     <p><code>  return 0;</code></p>
     <p><code> if (c == '.' || isdigit(c)) { /* number */</code></p>
     <p><code>  ungetc(c, stdin);</code></p>
     <p><code>  scanf("%lf", &amp;yylval.val);</code></p>
     <p><code>  return NUMBER;</code></p>
     <p><code> }</code></p>
     <p><code> if (islower(c)) {</code></p>
     <p><code>  yylval.index = c - 'a'; /* ASCII only */</code></p>
     <p><code>  return VAR;</code></p>
     <p><code> }</code></p>
     <p><code> if (c == '\n')</code></p>
     <p><code>  lineno++;</code></p>
     <p><code> return c;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>yyerror(s) /* report compile-time error */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, (char*)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>execerror(s, t) /* recover from run-time error */</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, t);</code></p>
     <p><code> longjmp(begin, 0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>fpecatch() /* catch floating point exceptions */</code></p>
     <p><code>{</code></p>
     <p><code> execerror("floating point exception", (char*)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>warning(s, t) /* print warning message */</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> fprintf(stderr. "%s: %s", progname, s);</code></p>
     <p><code> if (t &amp;&amp; *t)</code></p>
     <p><code>  fprintf(stderr, " %s , t);</code></p>
     <p><code> fprintf(stderr, " near line %d\n", lineno);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.2.2 <code>makefile</code></p>
     </title>
     <p><code>hoc2: hoc.o</code></p>
     <p><code>      cc hoc.o -o hoc2</code></p>
    </section>
   </section>
   <section>
    <title>
     <p>3.3 <code>hoc3</code></p>
    </title>
    <section>
     <title>
      <p>3.3.1 <code>makefile</code></p>
     </title>
     <p><code>YFLAGS = -d # force creation of y.tab.h</code></p>
     <p><code>OBJS = hoc.o init.o math.o symbol.o # abbreviation</code></p>
     <empty-line/>
     <p><code>hoc3: $(OBJS)</code></p>
     <p><code>      cc $(OBJS) -lm -o hoc3</code></p>
     <empty-line/>
     <p><code>hoc.o: hoc.h</code></p>
     <empty-line/>
     <p><code>init.o symbol.o: hoc.h y.tab.h</code></p>
     <empty-line/>
     <p><code>pr:</code></p>
     <p><code>      @pr hoc.y hoc.h init.c math.c symbol.c makefile</code></p>
     <empty-line/>
     <p><code>clean:</code></p>
     <p><code>      rm -f $(OBJS) y.tab.[ch]</code></p>
    </section>
    <section>
     <title>
      <p>3.3.2 <code>hoc.h</code></p>
     </title>
     <p><code>typedef struct Symbol { /* symbol table entry */</code></p>
     <p><code> char *name;</code></p>
     <p><code> short type; /* VAR, BLTIN, UNDEF */</code></p>
     <p><code> union {</code></p>
     <p><code>  double val; /* if VAR */</code></p>
     <p><code>  double (*ptr)(); /* if BLTIN */</code></p>
     <p><code> } u;</code></p>
     <p><code> struct Symbol *next; /* to link to another */</code></p>
     <p><code>} Symbol;</code></p>
     <p><code>Symbol *install(), *lookup();</code></p>
    </section>
    <section>
     <title>
      <p>3.3.3 <code>hoc.y</code></p>
     </title>
     <p><code>%{</code></p>
     <p><code>#include "hoc.h"</code></p>
     <p><code>extern double Pow();</code></p>
     <p><code>%}</code></p>
     <p><code>%union {</code></p>
     <p><code> double val; /* actual value */</code></p>
     <p><code> Symbol *sym; /* symbol table pointer */</code></p>
     <p><code>}</code></p>
     <p><code>%token &lt;val&gt; NUMBER</code></p>
     <p><code>%token &lt;sym&gt; VAR BLTIN UNDEF</code></p>
     <p><code>%type &lt;val&gt; expr asgn</code></p>
     <p><code>%right '=' %left '+' '-'</code></p>
     <p><code>%left '*' '/'</code></p>
     <p><code>%left UNARYMINUS</code></p>
     <p><code>%right /* exponentiation */</code></p>
     <p><code>%%</code></p>
     <p><code>list: /* nothing */</code></p>
     <p><code> | list '\n'</code></p>
     <p><code> | list asgn '\n'</code></p>
     <p><code> | list expr '\n' { printf("\t%.8g\n", $2); }</code></p>
     <p><code> | list error '\n' { yyerrok; }</code></p>
     <p><code> ;</code></p>
     <p><code>asgn: VAR '=' expr { $$=$1-&gt;u.val=$3; $1-&gt;type = VAR; }</code></p>
     <p><code> ;</code></p>
     <p><code>expr: NUMBER</code></p>
     <p><code> | VAR { if ($l-&gt;type == UNDEF)</code></p>
     <p><code>  execerror("undefined variable", $1-&gt;name);</code></p>
     <p><code>  $$ = $1-&gt;u.val;</code></p>
     <p><code> }</code></p>
     <p><code> | asgn</code></p>
     <p><code> | BLTIN '(' expr ')' { $$ = (*($1-&gt;u.ptr))($3); }</code></p>
     <p><code> | expr '+' expr { $$ = $1 + $3; }</code></p>
     <p><code> | expr '-' expr { $$ = $1 - $3; }</code></p>
     <p><code> | expr '*' expr { $$ = $1 * $3; }</code></p>
     <p><code> | expr '/' expr {</code></p>
     <p><code>  if ($3 == 0.0)</code></p>
     <p><code>  execerror("division by zero", "");</code></p>
     <p><code>  $$ = $1 / $3;</code></p>
     <p><code> }</code></p>
     <p><code> | expr '^' expr { $$ = Pow($1, $3); }</code></p>
     <p><code> | '(' expr ')' { $$ = $2; }</code></p>
     <p><code> | '-' expr %prec UNARYMINUS { $$ = -$2; }</code></p>
     <p><code> ;</code></p>
     <p><code>%%</code></p>
     <p><code>/* end of grammar */</code></p>
     <empty-line/>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;ctype.h&gt;</code></p>
     <p><code>char *progname;</code></p>
     <p><code>int lineno = 1;</code></p>
     <p><code>#include &lt;signal.h&gt;</code></p>
     <p><code>#include &lt;setjmp.h&gt;</code></p>
     <empty-line/>
     <p><code>jmp_buf begin;</code></p>
     <empty-line/>
     <p><code>main(argc, argv) /* hoc3 */</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> int fpecatch();</code></p>
     <p><code> progname = argv[0];</code></p>
     <p><code> init();</code></p>
     <p><code> setjmp(begin);</code></p>
     <p><code> signal(SIGFPE, fpecatch);</code></p>
     <p><code> yyparse();</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>yylex() /* hoc3 */</code></p>
     <p><code>{</code></p>
     <p><code> int c;</code></p>
     <empty-line/>
     <p><code> while ((c=getchar()) == ' ' || c == '\t')</code></p>
     <p><code>  ;</code></p>
     <p><code> if (c == EOF)</code></p>
     <p><code>  return 0;</code></p>
     <p><code> if (c == '.' || isdigit(c)) {</code></p>
     <p><code>  /* number */</code></p>
     <p><code>  ungetc(c, stdin);</code></p>
     <p><code>  scanf("%lf", &amp;yylval.val);</code></p>
     <p><code>  return NUMBER;</code></p>
     <p><code> }</code></p>
     <p><code> if (isalpha(c)) {</code></p>
     <p><code>  Symbol *s;</code></p>
     <p><code>  char sbuf[100], *p = sbuf;</code></p>
     <p><code>  do {</code></p>
     <p><code>   *p++ = c;</code></p>
     <p><code>  } while ((c=getchar()) != EOF &amp;&amp; isalnum(c));</code></p>
     <p><code>  ungetc(c, stdin);</code></p>
     <p><code>  *p = '\0';</code></p>
     <p><code>  if ((s=lookup(sbuf)) == 0)</code></p>
     <p><code>   s = install(sbuf, UNDEF, 0.0);</code></p>
     <p><code>  yylval.sym = s;</code></p>
     <p><code>  return s-&gt;type == UNDEF ? VAR : s-&gt;type;</code></p>
     <p><code> }</code></p>
     <p><code> if (c == '\n')</code></p>
     <p><code>  lineno++;</code></p>
     <p><code> return c;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>yyerror(s)</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, (char *)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>execerror(s, t) /* recover from run-time error */</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, t);</code></p>
     <p><code> longjmp(begin, 0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>fpecatch() /* catch floating point exceptions */</code></p>
     <p><code> execerror("floating point exception", (char*)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>warning(s, t)</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> fprintf(stderr, "%s: %s", progname, s);</code></p>
     <p><code> if (t &amp;&amp; *t)</code></p>
     <p><code>  fprintf (stderr, " %s", t);</code></p>
     <p><code> fprintf(stderr, " near line %d\n", lineno);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.3.4 <code>init.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <p><code>#include &lt;math.h&gt;</code></p>
     <empty-line/>
     <p><code>extern double Log(), Log10(), Exp(), Sqrt(), integer();</code></p>
     <empty-line/>
     <p><code>static struct { /* Constants */</code></p>
     <p><code> char *name;</code></p>
     <p><code> double eval;</code></p>
     <p><code>} consts[] = {</code></p>
     <p><code> "PI",    3.14159265358979323846,</code></p>
     <p><code> "E",     2.71828182845904523536,</code></p>
     <p><code> "GAMMA", 0.57721566490153286060, /* Euler */</code></p>
     <p><code> "DEG",  57.29577951308232087680, /* deg/radian */</code></p>
     <p><code> "PHI",   1.61803398874989484820, /* golden ratio */</code></p>
     <p><code> 0, 0</code></p>
     <p><code>};</code></p>
     <empty-line/>
     <p><code>static struct { /* Built-ins */</code></p>
     <p><code> char *name;</code></p>
     <p><code> double (*func)();</code></p>
     <p><code>} builtins[] = {</code></p>
     <p><code> "sin",   sin,</code></p>
     <p><code> "cos",   cos,</code></p>
     <p><code> "atan",  atan,</code></p>
     <p><code> "log",   Log, /* checks argument */</code></p>
     <p><code> "log10", Log10, /* checks argument */</code></p>
     <p><code> "exp",   Exp, /* checks argument */</code></p>
     <p><code> "sqrt",  Sqrt, /* checks argument */</code></p>
     <p><code> "int",   integer,</code></p>
     <p><code> "abs",   fabs,</code></p>
     <p><code> 0,       0</code></p>
     <p><code>};</code></p>
     <empty-line/>
     <p><code>init() /* install constants and built-ins in table */</code></p>
     <p><code>{</code></p>
     <p><code> int i;</code></p>
     <p><code> Symbol *s;</code></p>
     <empty-line/>
     <p><code> for (i = 0; consts[i].name; i++)</code></p>
     <p><code>  install(consts[i].name, VAR, consts[i].eval);</code></p>
     <p><code> for (i = 0; builtins[i].name; i++) {</code></p>
     <p><code>  s = install(builtins[i].name, BLTIN, 0.0);</code></p>
     <p><code>  s-&gt;u.ptr = builtins[i].func;</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.3.5 <code>math.c</code></p>
     </title>
     <p><code>#include &lt;math.h&gt;</code></p>
     <p><code>#include &lt;errno.h&gt;</code></p>
     <p><code>extern int errno;</code></p>
     <empty-line/>
     <p><code>double errcheck();</code></p>
     <empty-line/>
     <p><code>double Log(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(log(x), "log");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Log10(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(log10(x), "log10");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Sqrt(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(sqrt(x), "sqrt");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Exp(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(exp(x), "exp");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Pow(x, y)</code></p>
     <p><code> double x, y;</code></p>
     <p><code> return errcheck(pow(x,y), "exponentiation");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double integer(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return (double)(long)x;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double errcheck(d, s) /* check result of library call */</code></p>
     <p><code> double d;</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> if (errno == EDOM) {</code></p>
     <p><code>  errno = 0;</code></p>
     <p><code>  execerror(s, "argument out of domain");</code></p>
     <p><code> }</code></p>
     <p><code> else if (errno == ERANGE) {</code></p>
     <p><code>  errno = 0;</code></p>
     <p><code>  execerror(s, "result out of range");</code></p>
     <p><code> }</code></p>
     <p><code> return d;</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.3.6 <code>symbol.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <empty-line/>
     <p><code>static Symbol *symlist = 0; /* symbol table: linked list */</code></p>
     <empty-line/>
     <p><code>Symbol *lookup(s) /* find s in symbol table */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> Symbol *sp;</code></p>
     <empty-line/>
     <p><code> for (sp = symlist; sp != (Symbol*)0; sp = sp-&gt;next)</code></p>
     <p><code>  if (strcmp(sp-&gt;name, s) == 0)</code></p>
     <p><code>   return sp;</code></p>
     <p><code> return 0; /* 0 ==&gt; not found */</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>Symbol *install(s, t, d) /* install s in symbol table */</code></p>
     <p><code> char *s;</code></p>
     <p><code> int t;</code></p>
     <p><code> double d;</code></p>
     <p><code> Symbol *sp;</code></p>
     <p><code> char *emalloc();</code></p>
     <empty-line/>
     <p><code> sp = (Symbol*)emalloc(sizeof(Symbol));</code></p>
     <p><code> sp-&gt;name = emalloc(strlen(s)+1); /* +1 for '\0' */</code></p>
     <p><code> strcpy(sp-&gt;name, s);</code></p>
     <p><code> sp-&gt;type = t;</code></p>
     <p><code> sp-&gt;u.val = d;</code></p>
     <p><code> sp-&gt;next = symlist; /* put at front of list */</code></p>
     <p><code> symlist = sp;</code></p>
     <p><code> return sp;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>char *emalloc(n) /* check return from malloc */</code></p>
     <p><code> unsigned n;</code></p>
     <p><code>{</code></p>
     <p><code> char *p, *malloc();</code></p>
     <empty-line/>
     <p><code> p = malloc(n);</code></p>
     <p><code> if (p == 0)</code></p>
     <p><code>  execerror("out of memory", (char*)0);</code></p>
     <p><code> return p;</code></p>
     <p><code>}</code></p>
    </section>
   </section>
   <section>
    <title>
     <p>3.4 <code>hoc3</code> с <code>lex</code></p>
    </title>
    <section>
     <title>
      <p>3.4.1. <code>hoc.h</code></p>
     </title>
     <p><code>typedef struct Symbol { /* symbol table entry */</code></p>
     <p><code> char *name;</code></p>
     <p><code> short type; /* VAR, BLTIN, UNDEF */</code></p>
     <p><code> union {</code></p>
     <p><code>  double val; /* if VAR */</code></p>
     <p><code>  double (*ptr)(); /* if BLTIN */</code></p>
     <p><code> } u;</code></p>
     <p><code> struct Symbol *next; /* to link to another */</code></p>
     <p><code>} Symbol;</code></p>
     <p><code>Symbol *install(), *lookup();</code></p>
    </section>
    <section>
     <title>
      <p>3.4.2 <code>hoc.y</code></p>
     </title>
     <p><code>%{</code></p>
     <p><code>#include "hoc.h"</code></p>
     <p><code>extern double Pow();</code></p>
     <p><code>%}</code></p>
     <p><code>%union {</code></p>
     <p><code> double val; /* actual value */</code></p>
     <p><code> Symbol *sym; /* symbol table pointer */</code></p>
     <p><code>}</code></p>
     <p><code>%token &lt;val&gt; NUMBER</code></p>
     <p><code>%token &lt;sym&gt; VAR BLTIN UNDEF</code></p>
     <p><code>%type &lt;val&gt; expr asgn</code></p>
     <p><code>%right '='</code></p>
     <p><code>%left '+' '-'</code></p>
     <p><code>%left '*' '/'</code></p>
     <p><code>%left UNARYMINUS</code></p>
     <p><code>%right '^' /* exponentiation */</code></p>
     <p><code>%%</code></p>
     <p><code>list: /* nothing */</code></p>
     <p><code> | list '\n'</code></p>
     <p><code> | list asgn '\n'</code></p>
     <p><code> | list expr '\n' { printf("\t%.8g\n", $2); }</code></p>
     <p><code> | list error '\n' { yyerrok; }</code></p>
     <p><code> ;</code></p>
     <p><code>asgn: VAR '=' expr { $$=$1-&gt;u.val=$3; $1-&gt;type = VAR; }</code></p>
     <p><code> ;</code></p>
     <p><code>expr: NUMBER</code></p>
     <p><code> | VAR {</code></p>
     <p><code>  if ($1-&gt;type == UNDEF)</code></p>
     <p><code>   execerror("undefined variable", $1-&gt;name);</code></p>
     <p><code>  $$ = $1-&gt;u.val;</code></p>
     <p><code> }</code></p>
     <p><code> | asgn</code></p>
     <p><code> | BLTIN '(' expr ')' { $$ = (*($1-&gt;u.ptr))($3); }</code></p>
     <p><code> | expr '+' expr { $$ = $1 + $3; }</code></p>
     <p><code> | expr '+' expr { $$ = $1 - $3; }</code></p>
     <p><code> | expr '*' expr { $$ = $1 * $3; }</code></p>
     <p><code> | expr '/' expr {</code></p>
     <p><code>  if ($3 == 0.0)</code></p>
     <p><code>   execerror("division by zero", "");</code></p>
     <p><code>  $$ = $1 / $3;</code></p>
     <p><code> }</code></p>
     <p><code> | expr '^' expr { $$ = Pow($1, $3); }</code></p>
     <p><code> | '(' expr ')' { $$ = $2; }</code></p>
     <p><code> | '-' expr %prec UNARYMINUS { $$ = -$2; }</code></p>
     <p><code> ;</code></p>
     <p><code>%%</code></p>
     <p><code>/* end of grammar */</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;ctype.h&gt;</code></p>
     <p><code>char *progname;</code></p>
     <p><code>int lineno = 1;</code></p>
     <p><code>#include &lt;setjmp.h&gt;</code> </p>
     <empty-line/>
     <p><code>jmp_buf begin;</code></p>
     <empty-line/>
     <p><code>main(argc, argv) /* hoc3 */</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> progname = argv[0];</code></p>
     <p><code> init();</code></p>
     <p><code> setjmp(begin);</code></p>
     <p><code> yyparse();</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>yyerror(s)</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, (char *)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>execerror(s, t)</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, t);</code></p>
     <p><code> longjmp(begin, 0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>warning(s, t)</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> fprintf (stderr, "%s: %s", progname, s);</code></p>
     <p><code> if (t &amp;&amp; *t)</code></p>
     <p><code>  fprintf(stderr, " %s", t);</code></p>
     <p><code> fprintf(stderr, " near line %d\n", lineno);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.4.3 <code>init.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <p><code>#include &lt;math.h&gt;</code></p>
     <empty-line/>
     <p><code>extern double Log(), Log10(), Exp(), Sqrt(), integer();</code></p>
     <empty-line/>
     <p><code>static struct { /* Constants */</code></p>
     <p><code> char *name;</code></p>
     <p><code> double eval;</code></p>
     <p><code>} consts [] = {</code></p>
     <p><code> "PI",    3.14159265358979323846,</code></p>
     <p><code> "E",     2.71828182845904523536,</code></p>
     <p><code> "GAMMA", 0.57721566490153286060, /* Euler */</code></p>
     <p><code> "DEG",  57.29577951308232087680, /* deg/radian */</code></p>
     <p><code> "PHI",   1.61803398874989484820, /* golden ratio */</code></p>
     <p><code> 0,       0</code></p>
     <p><code>};</code></p>
     <empty-line/>
     <p><code>static struct { /* Built-ins */</code></p>
     <p><code> char *name;</code></p>
     <p><code> double (*func)();</code></p>
     <p><code>} builtins[] = {</code></p>
     <p><code> "sin",   sin,</code></p>
     <p><code> "cos",   cos,</code></p>
     <p><code> "atan",  atan,</code></p>
     <p><code> "log",   Log, /* checks argument */</code></p>
     <p><code> "log10", Log10, /* checks argument */</code></p>
     <p><code> "exp",   Exp, /* checks argument */</code></p>
     <p><code> "sqrt",  Sqrt, /* checks argument */</code></p>
     <p><code> "int",   integer,</code></p>
     <p><code> "abs",   fabs,</code></p>
     <p><code> 0,       0</code></p>
     <p><code>};</code></p>
     <empty-line/>
     <p><code>init() /* install constants and built-ins in table */</code></p>
     <p><code>{</code></p>
     <p><code> int i;</code></p>
     <p><code> Symbol *s;</code></p>
     <empty-line/>
     <p><code> for (i = 0; consts[i].name; i++)</code></p>
     <p><code>  install(consts[i].name, VAR, consts[i].eval);</code></p>
     <p><code> for (i = 0; builtins[i].name; i++) {</code></p>
     <p><code>  s = install(builtins[i].name, BLTIN, 0.0);</code></p>
     <p><code>  s-&gt;u.ptr = builtins[i].func;</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.4.4. <code>lex.l</code></p>
     </title>
     <p><code>%{</code></p>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <empty-line/>
     <p><code>extern int lineno;</code></p>
     <p><code>%}</code></p>
     <p><code>%%</code></p>
     <p><code>[ \t] { ; } /* skip blanks and tabs */</code></p>
     <p><code>[0-9]+\.?|[0-9][0-9]+ {</code></p>
     <p><code> sscanf(yytext, "%lf", &amp;yylval.val);</code></p>
     <p><code> return NUMBER;</code></p>
     <p><code>}</code></p>
     <p><code>[a-zA-Z][a-zA-Z0-9]* {</code></p>
     <p><code> Symbol *s;</code></p>
     <p><code> if ((s=lookup(yytext)) == 0)</code></p>
     <p><code>  s = install(yytext, UNDEF, 0.0);</code></p>
     <p><code> yylval.sym = s;</code></p>
     <p><code> return s-&gt;type == UNDEF ? VAR : s-&gt;type;</code></p>
     <p><code>}</code></p>
     <p><code>\n {</code></p>
     <p><code> lineno++;</code></p>
     <p><code> return '\n';</code></p>
     <p><code>} /* everything else */</code></p>
     <p><code>. { return yytext[0]; }</code></p>
    </section>
    <section>
     <title>
      <p>3.4.5 <code>makefile</code></p>
     </title>
     <p><code>YFLAGS = -d</code></p>
     <p><code>OBJS = hoc.o lex.o init.o math.o symbol.o</code></p>
     <empty-line/>
     <p><code>hoc3: $(OBJS)</code></p>
     <p><code>      cc $(OBJS) -lm -ll -o hoc3</code></p>
     <empty-line/>
     <p><code>hoc.o: hoc.h</code></p>
     <empty-line/>
     <p><code>lex.o init.o symbol.o: hoc.h y.tab.h</code></p>
    </section>
    <section>
     <title>
      <p>3.4.6 <code>math.c</code></p>
     </title>
     <p><code>#include &lt;math.h&gt;</code></p>
     <p><code>#include &lt;errno.h&gt;</code></p>
     <empty-line/>
     <p><code>extern int errno;</code></p>
     <p><code>double errcheck();</code></p>
     <empty-line/>
     <p><code>double Log(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(log(x), "log");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Log10(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(log10(x), "log10");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Sqrt(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(sqrt(x), "sqrt");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Exp(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(exp(x), "exp");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Pow(x, y)</code></p>
     <p><code> double x, y;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(pow(x,y), "exponentiation");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double integer(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return (double)(long)x;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double errcheck(d, s) /* check result of library call */</code></p>
     <p><code> double d;</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> if (errno == EDOM) {</code></p>
     <p><code>  errno = 0;</code></p>
     <p><code>  execerror(s, "argument out of domain");</code></p>
     <p><code> } else if (errno == ERANGE) {</code></p>
     <p><code>  errno = 0;</code></p>
     <p><code>  execerror(s, "result out of range");</code></p>
     <p><code> }</code></p>
     <p><code> return d;</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.4.7 <code>symbol.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <empty-line/>
     <p><code>static Symbol *symlist =0; /* symbol table: linked list */</code></p>
     <empty-line/>
     <p><code>Symbol *lookup(s) /* find s in symbol table */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> Symbol *sp;</code></p>
     <empty-line/>
     <p><code> for (sp = symlist; sp != (Symbol*)0; sp = sp-&gt;next)</code></p>
     <p><code>  if (strcmp(sp-&gt;name, s) == 0)</code></p>
     <p><code>   return sp;</code></p>
     <p><code> return 0; /* 0 ==&gt; not found */</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>Symbol *install(s, t, d) /* install s in symbol table */</code></p>
     <p><code> char *s;</code></p>
     <p><code> int t;</code></p>
     <p><code> double d;</code></p>
     <p><code>{</code></p>
     <p><code> Symbol *sp;</code></p>
     <p><code> char *emalloc();</code></p>
     <empty-line/>
     <p><code> sp = (Symbol*)emalloc(sizeof(Symbol));</code></p>
     <p><code> sp-&gt;name = emalloc(strlen(s)+1); /* +1 for '\0' */</code></p>
     <p><code> strcpy(sp-&gt;name, s);</code></p>
     <p><code> sp-&gt;type = t;</code></p>
     <p><code> sp-&gt;u.val = d;</code></p>
     <p><code> sp-&gt;next = symlist; /* put at front of list */</code></p>
     <p><code> symlist = sp;</code></p>
     <p><code> return sp;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>char *emalloc(n) /* check return from malloc */</code></p>
     <p><code> unsigned n;</code></p>
     <p><code>{</code></p>
     <p><code> char *p, *malloc();</code></p>
     <empty-line/>
     <p><code> p = malloc(n);</code></p>
     <p><code> if (p == 0)</code></p>
     <p><code>  execerror("out of memory", (char*)0);</code></p>
     <p><code> return p;</code></p>
     <p><code>}</code></p>
    </section>
   </section>
   <section>
    <title>
     <p>3.5 <code>hoc4</code></p>
    </title>
    <section>
     <title>
      <p>3.5.1 <code>code.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <empty-line/>
     <p><code>#define NSTACK 256</code></p>
     <p><code>static Datum stack[NSTACK]; /* the stack */</code></p>
     <p><code>static Datum *stackp; /* next free spot on stack */</code></p>
     <empty-line/>
     <p><code>#define NPROG 2000</code></p>
     <p><code>Inst prog[NPROG]; /* the machine */</code></p>
     <p><code>Inst *progp; /* next free spot for code generation */</code></p>
     <p><code>Inst *pc; /* program counter during execution */</code></p>
     <empty-line/>
     <p><code>initcode() /* initialize for code generation */</code></p>
     <p><code>{</code></p>
     <p><code> stackp = stack;</code></p>
     <p><code> progp = prog;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>push(d) /* push d onto stack */</code></p>
     <p><code> Datum d;</code></p>
     <p><code>{</code></p>
     <p><code> if (stackp &gt;= &amp;stack[NSTACK])</code></p>
     <p><code>  execerror("stack overflow", (char*)0);</code></p>
     <p><code> *stackp++ = d;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>Datum pop() /* pop and return top elem from stack */</code></p>
     <p><code>{</code></p>
     <p><code> if (stackp &lt;= stack)</code></p>
     <p><code>  execerror("stack underflow", (char*)0);</code></p>
     <p><code> return *--stackp;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>constpush() /* push constant onto stack */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d.val = ((Symbol*)*pc++)-&gt;u.val;</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>varpush() /* push variable onto stack */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d.sym = (Symbol*)(*pc++);</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>bltin() /* evaluate built-in on top of stack */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d = pop();</code></p>
     <p><code> d.val = (*(double(*)())(*pc++))(d.val);</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>eval() /* evaluate variable on stack */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d = pop() ;</code></p>
     <p><code> if (d.sym-&gt;type == UNDEF)</code></p>
     <p><code>  execerror("undefined variable", d.sym-&gt;name);</code></p>
     <p><code> d.val = d.sym-&gt;u.val;</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>add() /* add top two elems on stack */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val += d2.val;</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>sub() /* subtract top of stack from next */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val -= d2.val;</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>mul() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val *= d2.val;</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>div() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> if (d2.val == 0.0)</code></p>
     <p><code>  execerror("division by zero", (char*)0);</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val /= d2.val;</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>negate() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d = pop();</code></p>
     <p><code> d.val = -d.val;</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>power() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> extern double Pow();</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = Pow(d1.val, d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>assign() /* assign top value to next value */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d1= pop();</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> if (d1.sym-&gt;type != VAR &amp;&amp; d1.sym-&gt;type != UNDEF)</code></p>
     <p><code>  execerror("assignment to non-variable", d1.sym-&gt;name);</code></p>
     <p><code> d1.sym-&gt;u.val = d2.val;</code></p>
     <p><code> d1.sym-&gt;type = VAR;</code></p>
     <p><code> push(d2);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>print() /* pop top value from stack, print it */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d = pop();</code></p>
     <p><code> printf("\t%8g\n", d.val);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>Inst *code(f) /* install one instruction or operand */</code></p>
     <p><code> Inst f;</code></p>
     <p><code>{</code></p>
     <p><code> Inst *oprogp = progp;</code></p>
     <p><code> if (progp &gt;= &amp;eprog[NPROG])</code></p>
     <p><code>  execerror("program too big", (char*)0);</code></p>
     <p><code> *progp++ = f;</code></p>
     <p><code> return oprogp;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>execute(p) /* run the machine */</code></p>
     <p><code> Inst *p;</code></p>
     <p><code>{</code></p>
     <p><code> for (pc = p; *pc != STOP; )</code></p>
     <p><code>  (*(*pc++))();</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.5.2 <code>hoc.h</code></p>
     </title>
     <p><code>typedef struct Symbol { /* symbol table entry */</code></p>
     <p><code> char *name;</code></p>
     <p><code> short type; /* VAR, BLTIN, UNDEF */</code></p>
     <p><code> union {</code></p>
     <p><code>  double val; /* if VAR */</code></p>
     <p><code>  double (*ptr)(); /* if BLTIN */</code></p>
     <p><code> } u;</code></p>
     <p><code> struct Symbol *next; /* to link to another */</code></p>
     <p><code>} Symbol;</code></p>
     <p><code>Symbol *install(), *lookup();</code></p>
     <empty-line/>
     <p><code>typedef union Datum { /* interpreter stack type */</code></p>
     <p><code> double val;</code></p>
     <p><code> Symbol *sym;</code></p>
     <p><code>} Datum;</code></p>
     <p><code>extern Datum pop();</code></p>
     <empty-line/>
     <p><code>typedef int (*Inst)(); /* machine instruction */</code></p>
     <p><code>#define STOP (Inst)0</code></p>
     <p><code>extern Inst prog[];</code></p>
     <p><code>extern eval(), add(), sub(), mul(), div(), negate(), power();</code></p>
     <p><code>extern assign(), bltin(), varpush(), constpush(), print();</code></p>
    </section>
    <section>
     <title>
      <p>3.5.3 <code>hoc.y</code></p>
     </title>
     <p><code>%{</code></p>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#define code2(c1,c2) code(c1); code(c2)</code></p>
     <p><code>#define code3(c1,c2,c3) code(c1); code(c2); code(c3)</code></p>
     <p><code>%}</code></p>
     <p><code>%union {</code></p>
     <p><code> Symbol *sym; /* symbol table pointer */</code></p>
     <p><code> Inst *inst; /* machine instruction */</code></p>
     <p><code>}</code></p>
     <p><code>%token &lt;sym&gt; NUMBER VAR BLTIN UNDEF</code></p>
     <p><code>%right '='</code></p>
     <p><code>%left '+' '-'</code></p>
     <p><code>%left '*' '/'</code></p>
     <p><code>%left UNARYMINUS</code></p>
     <p><code>%right '^' /* exponentiation */</code></p>
     <p><code>%%</code></p>
     <p><code>list: /* nothing */</code></p>
     <p><code> | list '\n'</code></p>
     <p><code> | list asgn '\n' { code2(pop, STOP); return 1; }</code></p>
     <p><code> | list expr '\n' { code2(print, STOP); return 1; }</code></p>
     <p><code> | list error '\n' { yyerrok; }</code></p>
     <p><code> ;</code></p>
     <p><code>asgn: VAR '=' expr { code3(varpush,(Inst)$1.assign); }</code></p>
     <p><code> ;</code></p>
     <p><code>expr: NUMBER { code2(constpush, (Inst)$1); }</code></p>
     <p><code> | VAR { code3(varpush, (Inst)$1, eval); }</code></p>
     <p><code> | asgn</code></p>
     <p><code> | BLTIN '(' expr ')' { code2(bltin, (Inst)$1-&gt;u.ptr); }</code></p>
     <p><code> | '(' expr ')'</code></p>
     <p><code> | expr '+' expr { code(add); }</code></p>
     <p><code> | expr '-' expr { code(sub); }</code></p>
     <p><code> | expr '*' expr { code(mul); }</code></p>
     <p><code> | expr '/' expr { code(div); }</code></p>
     <p><code> | expr '^' expr { code(power); }</code></p>
     <p><code> | '-' expr %prec UNARYMINUS { code(negate); }</code></p>
     <p><code> ;</code></p>
     <p><code>%%</code></p>
     <p><code>/* end of grammar */</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;ctype.h&gt;</code></p>
     <p><code>char *progname;</code></p>
     <p><code>int lineno = 1;</code></p>
     <p><code>#include &lt;signal.h&gt;</code></p>
     <p><code>#include &lt;setjmp.h&gt;</code></p>
     <p><code>jmp_buf begin;</code></p>
     <empty-line/>
     <p><code>main(argc, argv) /* hoc4 */</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> int fpecatch();</code></p>
     <p><code> progname = argv[0];</code></p>
     <p><code> init();</code></p>
     <p><code> setjmp(begin);</code></p>
     <p><code> signal(SIGFPE, fpecatch);</code></p>
     <p><code> for (initcode(); yyparse(); initcode())</code></p>
     <p><code>  execute(prog);</code></p>
     <p><code> return 0;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>static int c; /* global for use by warning() */</code></p>
     <empty-line/>
     <p><code>yylex() /* hoc4 */</code></p>
     <p><code>{</code></p>
     <p><code> while ((c=getchar()) == ' ' || с == '\t')</code></p>
     <p><code>  ;</code></p>
     <p><code> if (c == EOF)</code></p>
     <p><code>  return 0;</code></p>
     <p><code> if (c == '.' || isdigit(c)) { /* number */</code></p>
     <p><code>  double d;</code></p>
     <p><code>  ungetc(c, stdin);</code></p>
     <p><code>  scanf ("%lf", &amp;d);</code></p>
     <p><code>  yylval.sym = install("", NUMBER, d);</code></p>
     <p><code>  return NUMBER;</code></p>
     <p><code> }</code></p>
     <p><code> if (isalpha(c)) {</code></p>
     <p><code>  Symbol *s;</code></p>
     <p><code>  char sbuf[100], *p = sbuf;</code></p>
     <p><code>  do {</code></p>
     <p><code>   *p++ = c;</code></p>
     <p><code>  } while ((c=getchar()) != EOF &amp;&amp; isalnum(c));</code></p>
     <p><code>  ungetc(c, stdin);</code></p>
     <p><code>  *p = '\0';</code></p>
     <p><code>  if ((s=lookup(sbuf)) == 0)</code></p>
     <p><code>   s = install(sbuf, UNDEF, 0.0);</code></p>
     <p><code>  yylval.sym = s;</code></p>
     <p><code>  return s-&gt;type == UNDEF ? VAR : s-&gt;type;</code></p>
     <p><code> }</code></p>
     <p><code> if (c == '\n')</code></p>
     <p><code> lineno++;</code></p>
     <p><code> return c;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>yyerror(s)</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, (char *)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>execerror(s, t) /* recover from run-time error */</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, t);</code></p>
     <p><code> longjmp(begin, 0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>fpecatch() /* catch floating point exceptions */</code></p>
     <p><code>{</code></p>
     <p><code> execerror("floating point exception", (char*)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>warning(s, t)</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> fprintf(stderr, "%s: %s", progname, s);</code></p>
     <p><code> if (t &amp;&amp; *t)</code></p>
     <p><code>  fprintf(stderr, " %s", t);</code></p>
     <p><code> fprintf(stderr, " near line %d\n", lineno);</code></p>
     <p><code> while (c != '\n' &amp;&amp; с != EOF)</code></p>
     <p><code>  с = getchar(); /* flush rest of input line */</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.5.4 <code>init.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <p><code>#include &lt;math.h&gt;</code></p>
     <empty-line/>
     <p><code>extern double Log(), Log10(), Exp(), Sqrt(), integer();</code></p>
     <empty-line/>
     <p><code>static struct { /* Constants */</code></p>
     <p><code> char *name;</code></p>
     <p><code> double eval;</code></p>
     <p><code>} consts[] = {</code></p>
     <p><code> "PI",    3.14159265358979323846,</code></p>
     <p><code> "E",     2.71828182845904523536,</code></p>
     <p><code> "GAMMA", 0.57721566490153286060, /* Euler */</code></p>
     <p><code> "DEG",  57.29577951308232087680, /* deg/radian */</code></p>
     <p><code> "PHI",   1.61803398874989484820, /* golden ratio */</code></p>
     <p><code> 0,       0</code></p>
     <p><code>};</code></p>
     <empty-line/>
     <p><code>static struct { /* Built-ins */</code></p>
     <p><code> char *name;</code></p>
     <p><code> double (*func)();</code></p>
     <p><code>} builtins [] = {</code></p>
     <p><code> "sin",   sin,</code></p>
     <p><code> "cos",   cos,</code></p>
     <p><code> "atan",  atan,</code></p>
     <p><code> "log",   Log, /* checks argument */</code></p>
     <p><code> "log10", Log10, /* checks argument */</code></p>
     <p><code> "exp",   Exp, /* checks argument */</code></p>
     <p><code> "sqrt",  Sqrt, /* checks argument */</code></p>
     <p><code> "int",   integer,</code></p>
     <p><code> "abs",   fabs,</code></p>
     <p><code> 0,       0</code></p>
     <p><code>};</code></p>
     <empty-line/>
     <p><code>init() /* install constants and built-ins in table */</code></p>
     <p><code>{</code></p>
     <p><code> int i;</code></p>
     <p><code> Symbol *s;</code></p>
     <empty-line/>
     <p><code> for (i = 0; consts[i].name; i++)</code></p>
     <p><code>  install(consts[i].name, VAR, consts[i].eval);</code></p>
     <p><code> for (i = 0; builtins[i].name; i++) {</code></p>
     <p><code>  s = install(builtins[i].name, BLTIN, 0.0);</code></p>
     <p><code>  s-&gt;u.ptr = builtins[i].func;</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.5.5. <code>makefile</code></p>
     </title>
     <p><code>YFLAGS = -d</code></p>
     <p><code>OBJS = hoc.o code.o init.o math.o symbol.o</code></p>
     <empty-line/>
     <p><code>hoc4: $(OBJS)</code></p>
     <p><code>      cc $(OBJS) -lm -o hoc4</code></p>
     <empty-line/>
     <p><code>hoc.o code.o init.o symbol.o: hoc.h</code></p>
     <empty-line/>
     <p><code>code.o init.o symbol.o: x.tab.h</code></p>
     <empty-line/>
     <p><code>x.tab.h: y.tab.h</code></p>
     <p><code>      -cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h</code></p>
     <empty-line/>
     <p><code>pr: hoc.y hoc.h code.c init.c math.c symbol.c</code></p>
     <p><code>      @pr $?</code></p>
     <p><code>      @touch pr</code></p>
     <empty-line/>
     <p><code>clean:</code></p>
     <p><code>      rm -f $(OBJS) [xy].tab.[ch]</code></p>
    </section>
    <section>
     <title>
      <p>3.5.6. <code>math.c</code></p>
     </title>
     <p><code>#include &lt;math.h&gt;</code></p>
     <p><code>#include &lt;errno.h&gt;</code></p>
     <empty-line/>
     <p><code>extern int errno;</code></p>
     <empty-line/>
     <p><code>double errcheck();</code></p>
     <empty-line/>
     <p><code>double Log(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(log(x), "log");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Log10(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(log10(x), "log10");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Sqrt(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(sqrt(x), "sqrt");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Exp(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(exp(x), "exp");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Pow(x, y)</code></p>
     <p><code> double x, y;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(pow(x,y), "exponentiation");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double integer(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return (double)(long)x;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double errcheck(d, s) /* check result of library call */</code></p>
     <p><code> double d;</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> if (errno == EDOM) {</code></p>
     <p><code>  errno = 0;</code></p>
     <p><code>  execerror(s, "argument out of domain");</code></p>
     <p><code> } else if (errno == ERANGE) {</code></p>
     <p><code>  errno = 0;</code></p>
     <p><code>  execerror(s, "result out of range");</code></p>
     <p><code> }</code></p>
     <p><code> return d;</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.5.7 <code>symbol.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <empty-line/>
     <p><code>static Symbol *symlist = 0; /* symbol table: linked list */</code></p>
     <empty-line/>
     <p><code>Symbol *lookup(s) /* find s in symbol table */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> Symbol *sp;</code></p>
     <empty-line/>
     <p><code> for (sp = symlist; sp != (Symbol*)0; sp = sp-&gt;next)</code></p>
     <p><code>  if (strcmp(sp-&gt;name, s) == 0)</code></p>
     <p><code>   return sp;</code></p>
     <p><code> return 0; /* 0 ==&gt; not found */</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>Symbol *install(s, t, d) /* install s in symbol table */</code></p>
     <p><code> char *s;</code></p>
     <p><code> int t;</code></p>
     <p><code> double d;</code></p>
     <p><code>{</code></p>
     <p><code> Symbol *sp;</code></p>
     <p><code> char *emalloc();</code></p>
     <empty-line/>
     <p><code> sp = (Symbol*)emalloc(sizeof(Symbol));</code></p>
     <p><code> sp-&gt;name = emalloc(strlen(s)+1); /* +1 for '\0' */</code></p>
     <p><code> strcpy(sp-&gt;name, s);</code></p>
     <p><code> sp-&gt;type = t;</code></p>
     <p><code> sp-&gt;u.val = d;</code></p>
     <p><code> sp-&gt;next = symlist; /* put at front of list */</code></p>
     <p><code> symlist = sp;</code></p>
     <p><code> return sp;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>char *emalloc(n) /* check return from malloc */</code></p>
     <p><code> unsigned n;</code></p>
     <p><code>{</code></p>
     <p><code> char *p, *malloc();</code></p>
     <empty-line/>
     <p><code> p = malloc(n);</code></p>
     <p><code> if (p == 0)</code></p>
     <p><code>  execerror("out of memory", (char*)0);</code></p>
     <p><code> return p;</code></p>
     <p><code>}</code></p>
    </section>
   </section>
   <section>
    <title>
     <p>3.6 <code>hoc5</code></p>
    </title>
    <section>
     <title>
      <p>3.6.1 <code>code.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <empty-line/>
     <p><code>#define NSTACK 256</code></p>
     <p><code>static Datum stack[NSTACK];</code></p>
     <p><code>static Datum *stackp;</code></p>
     <empty-line/>
     <p><code>#define NPROG 2000</code></p>
     <p><code>Inst prog[NPROG];</code></p>
     <p><code>static Inst *pc;</code></p>
     <p><code>Inst *progp;</code></p>
     <empty-line/>
     <p><code>initcode() {</code></p>
     <p><code> progp = prog;</code></p>
     <p><code> stackp = stack;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>push(d)</code></p>
     <p><code> Datum d;</code></p>
     <p><code>{</code></p>
     <p><code> if (stackp &gt;= &amp;stack[NSTACK])</code></p>
     <p><code>  execerror("stack too deep", (char*)0);</code></p>
     <p><code> *stackp++ = d;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>Datum pop() {</code></p>
     <p><code> if (stackp == stack)</code></p>
     <p><code>  execerror("stack underflow", (char*)0);</code></p>
     <p><code> return *--stackp;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>constpush() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d.val = ((Symbol*)*pc++)-&gt;u.val;</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>varpush() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d.sym = (Symbol*)(*pc++);</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>whilecode() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> Inst *savepc = pc; /* loop body */</code></p>
     <p><code> execute(savepc+2); /* condition */</code></p>
     <p><code> d = pop();</code></p>
     <p><code> while (d.val) {</code></p>
     <p><code>  execute (*((Inst**)(savepc))); /* body */</code></p>
     <p><code>  execute(savepc+2);</code></p>
     <p><code>  d = pop();</code></p>
     <p><code> }</code></p>
     <p><code> pc = *((Inst**)(savepc+1)); /* next statement */</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>ifcode() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> Inst *savepc = pc; /* then part */</code></p>
     <p><code> execute(savepc+3); /* condition */</code></p>
     <p><code> d = pop();</code></p>
     <p><code> if (d.val)</code></p>
     <p><code>  execute(*((Inst**)(savepc)));</code></p>
     <p><code> else if (*((Inst**)(savepc+1))) /* else part? */</code></p>
     <p><code>  execute(*((Inst**)(savepc+1)));</code></p>
     <p><code> pc = *((Inst**)(savepc+2)); /* next stmt */</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>bltin() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d = pop();</code></p>
     <p><code> d.val = (*(double(*)())(*pc++))(d.val);</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>eval() /* Evaluate variable on stack */ {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d = pop();</code></p>
     <p><code> if (d.sym-&gt;type != VAR &amp;&amp; d.sym-&gt;type != UNDEF)</code></p>
     <p><code>  execerror("attempt to evaluate non-variable", d.sym-&gt;name);</code></p>
     <p><code> if (d.sym-&gt;type == UNDEF)</code></p>
     <p><code>  execerror("undefined variable", d.sym-&gt;name);</code></p>
     <p><code> d.val = d.sym-&gt;u.val;</code></p>
     <p><code>  push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>add() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val += d2.val;</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>sub() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val -= d2.val;</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>mul() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val *= d2.val;</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>div() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> if (d2.val == 0.0)</code></p>
     <p><code>  execerror("division by zero", (char*)0);</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val /= d2.val;</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>negate() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d = pop();</code></p>
     <p><code> d.val = -d.val;</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>gt() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val &gt; d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>lt() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val &lt; d2.val);</code> </p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>ge() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val &gt;= d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>le() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val &lt;= d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>eq() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val == d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>ne() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val != d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>and() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val != 0.0 &amp;&amp; d2.val != 0.0);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>or() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val != 0.0 || d2.val != 0.0);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>not() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d = pop();</code></p>
     <p><code> d.val = (double)(d.val == 0.0);</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>power() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> extern double Pow();</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = Pow(d1.val, d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>assign() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> if (d1.sym-&gt;type != VAR &amp;&amp; d1.sym-&gt;type != UNDEF)</code></p>
     <p><code>  execerror("assignment to non-variable", d1.sym-&gt;name);</code></p>
     <p><code> d1.sym-&gt;u.val = d2.val;</code></p>
     <p><code> d1.sym-&gt;type = VAR;</code></p>
     <p><code> push(d2);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>print() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d = pop();</code></p>
     <p><code> printf("\t%.8g\n", d.val);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>prexpr() /* print numeric value */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d = pop();</code></p>
     <p><code> printf("%.8g\n", d.val);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>Inst *code(f) /* install one instruction or operand */</code></p>
     <p><code> Inst f;</code></p>
     <p><code>{</code></p>
     <p><code> Inst *oprogp = progp;</code></p>
     <p><code> if (progp &gt;= &amp;prog[NPROG])</code></p>
     <p><code>  execerror("expression too complicated", (char*)0);</code></p>
     <p><code> *progp++ = f;</code></p>
     <p><code> return oprogp;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>execute(p)</code></p>
     <p><code> Inst *p;</code></p>
     <p><code>{</code></p>
     <p><code> for (pc = p; *pc != STOP; ) (*(*pc++))();</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.6.2 <code>fib</code></p>
     </title>
     <p><code>{</code></p>
     <p><code> a=0</code></p>
     <p><code> b=1</code></p>
     <p><code> while(b&lt;1000) {</code></p>
     <p><code>  c=b</code></p>
     <p><code>  b=a+b</code></p>
     <p><code>  a=c</code></p>
     <p><code>  print(c)</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.6.3 <code>fib2</code></p>
     </title>
     <p><code>{</code></p>
     <p><code> n=0</code></p>
     <p><code> a=0</code></p>
     <p><code> b=1</code></p>
     <p><code> while(b&lt;10000000){</code></p>
     <p><code>  n=n+1</code></p>
     <p><code>  c=b</code></p>
     <p><code>  b=a+b</code></p>
     <p><code>  a=c</code></p>
     <p><code>  print(b)</code></p>
     <p><code> }</code></p>
     <p><code> print(n)</code> </p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.6.4 <code>hoc.h</code></p>
     </title>
     <p><code>typedef struct Symbol { /* symbol table entry */</code></p>
     <p><code> char *name;</code></p>
     <p><code> short type; /* VAR, BLTIN, UNDEF */</code></p>
     <p><code> union {</code></p>
     <p><code>  double val; /* if VAR */</code></p>
     <p><code>  double (*ptr)(); /* if BLTIN */</code></p>
     <p><code> } u;</code></p>
     <p><code> struct Symbol *next; /* to link to another */</code></p>
     <p><code>} Symbol;</code></p>
     <p><code>Symbol *install(), *lookup();</code></p>
     <empty-line/>
     <p><code>typedef union Datum { /* interpreter stack type */</code></p>
     <p><code> double val;</code></p>
     <p><code> Symbol *sym;</code></p>
     <p><code>} Datum;</code></p>
     <p><code>extern Datum pop();</code></p>
     <empty-line/>
     <p><code>typedef int (*Inst)(); /* machine instruction */</code></p>
     <p><code>#define STOP (Inst)0</code></p>
     <empty-line/>
     <p><code>extern Inst prog[], *progp, *code();</code></p>
     <p><code>extern eval(), add(), sub(), mul(), div(), negate(), power();</code></p>
     <p><code>extern assign(), bltin(), varpush(), constpush(), print();</code></p>
     <p><code>extern prexpr();</code></p>
     <p><code>extern gt(), lt(), eq(), ge(), le(), ne(), and(), or(), not();</code></p>
     <p><code>extern ifcode(), whilecode();</code></p>
    </section>
    <section>
     <title>
      <p>3.6.5 <code>hoc.y</code></p>
     </title>
     <p><code>%{</code></p>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#define code2(c1,c2) code(c1); code(c2)</code></p>
     <p><code>#define code3(c1,c2,c3) code(c1); code(c2); code(c3)</code></p>
     <p><code>%}</code></p>
     <p><code>%union {</code></p>
     <p><code> Symbol *sym; /* symbol table pointer */</code></p>
     <p><code> Inst *inst; /* machine instruction */</code></p>
     <p><code>}</code></p>
     <p><code>%token &lt;sym&gt; NUMBER PRINT VAR BLTIN UNDEF WHILE IF ELSE</code></p>
     <p><code>%type &lt;inst&gt; stmt asgn expr stmtlist cond while if end</code></p>
     <p><code>%right '='</code></p>
     <p><code>%left OR</code></p>
     <p><code>%left AND</code></p>
     <p><code>%left GT GE LT LE EQ NE</code></p>
     <p><code>%left '+' '-'</code></p>
     <p><code>%left '*' '/'</code></p>
     <p><code>%left UNARYMINUS NOT</code></p>
     <p><code>%right</code></p>
     <p><code>%%</code></p>
     <p><code>list: /* nothing */</code></p>
     <p><code> | list '\n'</code></p>
     <p><code> | list asgn '\n' { code2(pop, STOP); return 1; }</code></p>
     <p><code> | list stmt '\n' { code(STOP); return 1; }</code></p>
     <p><code> | list expr '\n' { code2(print, STOP); return 1; }</code></p>
     <p><code> | list error '\n' { yyerrok; }</code></p>
     <p><code> ;</code></p>
     <p><code>asgn: VAR '=' expr { $$=$3; code3(varpush,(Inst)$1.assign); }</code></p>
     <p><code> ;</code></p>
     <p><code>stmt: expr { code(pop); }</code></p>
     <p><code> | PRINT expr { code(prexpr); $$ = $2; }</code></p>
     <p><code> | while cond stmt end {</code></p>
     <p><code>  ($1)[1] = (Inst)$3; /* body of loop */</code></p>
     <p><code>  ($1)[2] = (Inst)$4;</code></p>
     <p><code> } /* end, if cond fails */</code></p>
     <p><code> | if cond stmt end { /* else-less if */</code></p>
     <p><code>  ($1)[1] = (Inst)$3; /* thenpart */</code></p>
     <p><code>  ($1)[2] = (Inst)$4;</code></p>
     <p><code> } /* end, if cond fails */</code></p>
     <p><code> | if cond stmt end ELSE stmt end { /* if with else */</code></p>
     <p><code>  ($1)[1] = (Inst)$3; /* thenpart */</code></p>
     <p><code>  ($1)[2] = (Inst)$6; /* elsepart */</code></p>
     <p><code>  ($1)[3] = (Inst)$7;</code></p>
     <p><code> } /* end, if cond fails */</code></p>
     <p><code> | '{' stmtlist '}' { $$ = $2; }</code></p>
     <p><code> ;</code></p>
     <p><code>cond: '(' expr ')' { code(STOP); $$ = $2; }</code></p>
     <p><code> ;</code></p>
     <p><code>while: WHILE { $$ = code3(whilecode, STOP, STOP); }</code></p>
     <p><code> ;</code></p>
     <p><code>if: IF { $$=code(ifcode); code3(STOP, STOP, STOP); }</code></p>
     <p><code> ;</code></p>
     <p><code>end: /* nothing */ { code(STOP); $$ = progp; }</code></p>
     <p><code> ;</code></p>
     <p><code>stmtlist: /* nothing */ { $$ = progp; }</code></p>
     <p><code> | stmtlist '\n'</code></p>
     <p><code> | stmtlist stmt</code></p>
     <p><code> ;</code></p>
     <p><code>expr: NUMBER { $$ = code2(constpush, (Inst)$1); }</code></p>
     <p><code> | VAR { $$ = code3(varpush, (Inst)$1, eval); }</code></p>
     <p><code> | asgn</code></p>
     <p><code> | BLTIN '(' expr ')'</code></p>
     <p><code>  { $$ = $3; code2(bltin,(Inst)$1-&gt;u.ptr); }</code></p>
     <p><code> | '(' expr ')' { $$ = $2; }</code></p>
     <p><code> | expr '+' expr { code(add); }</code></p>
     <p><code> | expr '-' expr { code(sub); }</code></p>
     <p><code> | expr '*' expr { code(mul); }</code></p>
     <p><code> | expr '/' expr { code(div); }</code></p>
     <p><code> | expr '^' expr { code (power); }</code></p>
     <p><code> | '-' expr %prec UNARYMINUS { $$ = $2; code(negate); }</code></p>
     <p><code> | expr GT expr { code(gt); }</code></p>
     <p><code> | expr GE expr { code(ge); }</code></p>
     <p><code> | expr LT expr { code(lt); }</code></p>
     <p><code> | expr LE expr { code(le); }</code></p>
     <p><code> | expr EQ expr { code(eq); }</code></p>
     <p><code> | expr NE expr { code(ne); }</code></p>
     <p><code> | expr AND expr { code(and); }</code></p>
     <p><code> | expr OR expr { code(or); }</code></p>
     <p><code> | NOT expr { $$ = $2; code(not); }</code></p>
     <p><code> ;</code></p>
     <p><code>%%</code></p>
     <p><code>/* end of grammar */</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;ctype.h&gt;</code></p>
     <p><code>char *progname;</code></p>
     <p><code>int lineno = 1;</code></p>
     <p><code>#include &lt;signal.h&gt;</code></p>
     <p><code>#include &lt;setjmp.h&gt;</code></p>
     <empty-line/>
     <p><code>jmp_buf begin;</code></p>
     <p><code>int defining;</code></p>
     <empty-line/>
     <p><code>int c; /* global for use by warning() */</code></p>
     <p><code>yylex() /* hoc5 */</code></p>
     <p><code>{</code></p>
     <p><code> while ((c=getchar()) == ' ' || c == '\t')</code></p>
     <p><code>  ;</code></p>
     <p><code> if (c == EOF)</code></p>
     <p><code>  return 0;</code></p>
     <p><code> if (c == '.' || isdigit(c)) { /* number */</code></p>
     <p><code>  double d;</code></p>
     <p><code>  ungetc(c, stdin);</code></p>
     <p><code>  scanf("%lf", &amp;d);</code></p>
     <p><code>  yylval.sym = install("", NUMBER, d);</code></p>
     <p><code>  return NUMBER;</code></p>
     <p><code> }</code></p>
     <p><code> if (isalpha(c)) {</code></p>
     <p><code>  Symbol *s;</code></p>
     <p><code>  char sbuf[100], *p = sbuf;</code></p>
     <p><code>  do</code></p>
     <p><code>   *p++ = c;</code></p>
     <p><code>  while ((c=getchar()) != EOF &amp;&amp; isalnum(c));</code></p>
     <p><code>  ungetc(c, stdin);</code></p>
     <p><code>  *p = '\0';</code></p>
     <p><code>  if ((s=lookup(sbuf)) == 0)</code></p>
     <p><code>   s = install(sbuf, UNDEF, 0.0);</code></p>
     <p><code>  yylval.sym = s;</code></p>
     <p><code>  return s-&gt;type == UNDEF ? VAR : s-&gt;type;</code></p>
     <p><code> }</code></p>
     <p><code> switch (c) {</code></p>
     <p><code> case '&gt;': return follow('=', GE, GT);</code></p>
     <p><code> case '&lt;': return follow('=', LE, LT);</code></p>
     <p><code> case '=': return follow('=', EQ, '=');</code></p>
     <p><code> case '!': return follow('=', NE, NOT);</code></p>
     <p><code> case '|': return follow('|', OR, '|');</code></p>
     <p><code> case '&amp;': return follow('&amp;', AND, '&amp;');</code></p>
     <p><code> case '\n': lineno++; return '\n';</code></p>
     <p><code> default: return c;</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>follow(expect, ifyes, ifno) /* look ahead for &gt;=, etc. */</code></p>
     <p><code>{</code></p>
     <p><code> int c = getchar();</code></p>
     <p><code> if (c == expect)</code></p>
     <p><code>  return ifyes;</code></p>
     <p><code> ungetc(c, stdin);</code></p>
     <p><code> return ifno;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>yyerror(s)</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, (char*)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>execerror(s, t) /* recover from run-time error */</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, t);</code></p>
     <p><code> longjmp(begin, 0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>fpecatch() /* catch floating point exceptions */</code></p>
     <p><code>{</code></p>
     <p><code> execerror("floating point exception", (char*)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> int fpecatch();</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> init();</code></p>
     <p><code> setjmp(begin);</code></p>
     <p><code> signal(SIGFPE, fpecatch);</code></p>
     <p><code> for (initcode(); yyparse(); initcode())</code></p>
     <p><code>  execute(prog);</code></p>
     <p><code> return 0;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>warning(s, t)</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> fprintf(stderr, "%s: %s", progname, s);</code></p>
     <p><code> if (t &amp;&amp; *t)</code></p>
     <p><code>  fprintf(stderr, " %s", t);</code></p>
     <p><code> fprintf(stderr, " near line %d\n", lineno);</code></p>
     <p><code> while (c != '\n' &amp;&amp; с != EOF)</code></p>
     <p><code>  c = getchar(); /* flush rest of input line */</code></p>
     <p><code> fseek(stdin, 0L, 2); /* flush rest of file */</code></p>
     <p><code> longjmp(begin, 0);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.6.6 <code>init.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <p><code>#include &lt;math.h&gt;</code></p>
     <empty-line/>
     <p><code>extern double Log(), Log10(), Sqrt(), Exp(), integer();</code></p>
     <empty-line/>
     <p><code>static struct { /* Keywords */</code></p>
     <p><code> char *name;</code></p>
     <p><code> int kval;</code></p>
     <p><code>} keywords[] = {</code></p>
     <p><code> "if",    IF,</code></p>
     <p><code> "else",  ELSE,</code></p>
     <p><code> "while", WHILE,</code></p>
     <p><code> "print", PRINT,</code></p>
     <p><code> 0,       0,</code></p>
     <p><code>};</code></p>
     <empty-line/>
     <p><code>static struct { /* Constants */</code></p>
     <p><code> char *name;</code></p>
     <p><code> double eval;</code></p>
     <p><code>} consts[] = {</code></p>
     <p><code> "PI",    3.14159265358979323846,</code></p>
     <p><code> "E",     2.71828182845904523536,</code></p>
     <p><code> "GAMMA", 0.57721566490153286060, /* Euler */</code></p>
     <p><code> "DEG",  57.29577951308232087680, /* deg/radian */</code></p>
     <p><code> "PHI",   1.61803398874989484820, /* golden ratio */</code></p>
     <p><code> 0,       0</code></p>
     <p><code>};</code></p>
     <empty-line/>
     <p><code>static struct { /* Built-ins */</code></p>
     <p><code> char *name;</code></p>
     <p><code> double (*func)();</code></p>
     <p><code>} builtins[] = {</code></p>
     <p><code> "sin",   sin,</code></p>
     <p><code> "cos",   cos,</code></p>
     <p><code> "atan",  atan,</code></p>
     <p><code> "log",   Log, /* checks argument */</code></p>
     <p><code> "log10", Log10, /* checks argument */</code></p>
     <p><code> "exp",   exp,</code></p>
     <p><code> "sqrt",  Sqrt, /* checks argument */</code></p>
     <p><code> "int",   integer,</code></p>
     <p><code> "abs",   fabs,</code></p>
     <p><code> 0, 0</code></p>
     <p><code>};</code></p>
     <empty-line/>
     <p><code>init() /* install constants and built-ins in table */</code></p>
     <p><code>{</code></p>
     <p><code> int i;</code></p>
     <p><code> Symbol *s;</code></p>
     <empty-line/>
     <p><code> for (i = 0; keywords[i].name; i++)</code></p>
     <p><code>  install(keywords[i].name, keywords[i].kval, 0.0);</code></p>
     <p><code> for (i = 0; consts[i].name; i++)</code></p>
     <p><code>  install(consts[i].name, VAR, consts[i].eval);</code></p>
     <p><code> for (i = 0; builtins[i].name; i++) {</code></p>
     <p><code>  s = install(builtins[i].name, BLTIN, 0.0);</code></p>
     <p><code>  s-&gt;u.ptr = builtins[i].func;</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.6.7 <code>makefile</code></p>
     </title>
     <p><code>YFLAGS = -d</code></p>
     <p><code>OBJS = hoc.o code.o init.o math.o symbol.o</code></p>
     <empty-line/>
     <p><code>hoc5: $(OBJS)</code></p>
     <p><code>      cc $(OBJS) -lm -o hoc5</code></p>
     <empty-line/>
     <p><code>hoc.o code.o init.o symbol.o: hoc.h</code></p>
     <empty-line/>
     <p><code>code.o init.o symbol.o: x.tab.h</code></p>
     <empty-line/>
     <p><code>x.tab.h: y.tab.h</code></p>
     <p><code>      -cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h</code></p>
     <empty-line/>
     <p><code>pr: hoc.y hoc.h code.c init.c math.c symbol.c</code></p>
     <p><code>      @pr $?</code></p>
     <p><code>      @touch pr</code></p>
     <empty-line/>
     <p><code>clean:</code></p>
     <p><code>      rm -f $(OBJS) [xy].tab.[ch]</code></p>
    </section>
    <section>
     <title>
      <p>3.6.8 <code>math.c</code></p>
     </title>
     <p><code>#include &lt;math.h&gt;</code></p>
     <p><code>#include &lt;errno.h&gt;</code></p>
     <empty-line/>
     <p><code>extern int errno;</code></p>
     <p><code>double errcheck();</code></p>
     <empty-line/>
     <p><code>double Log(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(log(x), "log");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Log10(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(log10(x), "log10");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Sqrt(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(sqrt(x), "sqrt");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Exp(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(exp(x), "exp");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Pow(x, y)</code></p>
     <p><code> double x, y;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(pow(x,y), "exponentiation");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double integer(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return (double)(long)x;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double errcheck(d, s) /* check result of library call */</code></p>
     <p><code> double d;</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> if (errno == EDOM) {</code></p>
     <p><code>  errno = 0;</code></p>
     <p><code>  execerror(s, "argument out of domain");</code></p>
     <p><code> } else if (errno == ERANGE) {</code></p>
     <p><code>  errno = 0;</code></p>
     <p><code>  execerror(s, "result out of range");</code></p>
     <p><code> }</code></p>
     <p><code> return d;</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.6.9 <code>symbol.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <empty-line/>
     <p><code>static Symbol *symlist =0; /* symbol table: linked list */</code></p>
     <empty-line/>
     <p><code>Symbol *lookup(s) /* find s in symbol table */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> Symbol *sp;</code></p>
     <empty-line/>
     <p><code> for (sp = symlist; sp != (Symbol*)0; sp = sp-&gt;next)</code></p>
     <p><code>  if (strcmp(sp-&gt;name, s) == 0)</code></p>
     <p><code>   return sp;</code></p>
     <p><code> return 0; /* 0 ==&gt; not found */</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>Symbol *install(s, t, d) /* install s in symbol table */</code></p>
     <p><code> char *s;</code></p>
     <p><code> int t;</code></p>
     <p><code> double d;</code></p>
     <p><code>{</code></p>
     <p><code> Symbol *sp;</code></p>
     <p><code> char *emalloc();</code></p>
     <empty-line/>
     <p><code> sp = (Symbol*)emalloc(sizeof(Symbol));</code></p>
     <p><code> sp-&gt;name = emalloc(strlen(s)+1); /* +1 for '\0' */</code></p>
     <p><code> strcpy(sp-&gt;name, s);</code></p>
     <p><code> sp-&gt;type = t;</code></p>
     <p><code> sp-&gt;u.val = d;</code></p>
     <p><code> sp-&gt;next = symlist; /* put at front of list */</code></p>
     <p><code> symlist = sp;</code></p>
     <p><code> return sp;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>char *emalloc(n) /* check return from malloc */</code></p>
     <p><code> unsigned n;</code></p>
     <p><code>{</code></p>
     <p><code> char *p, *malloc();</code></p>
     <empty-line/>
     <p><code> p = malloc(n);</code></p>
     <p><code> if (p == 0)</code></p>
     <p><code>  execerror("out of memory", (char*)0);</code></p>
     <p><code> return p;</code></p>
     <p><code>}</code></p>
    </section>
   </section>
   <section>
    <title>
     <p>3.7 <code>hoc6</code></p>
    </title>
    <section>
     <title>
      <p>3.7.1 <code>ack</code></p>
     </title>
     <p><code>func ack() {</code></p>
     <p><code> n = n+1</code></p>
     <p><code> if ($1 == 0) return ($2+1)</code></p>
     <p><code> if ($2 == 0) return (ack($1 - 1, 1))</code></p>
     <p><code> return (ack($1 - 1, ack($1, $2 - 1)))</code></p>
     <p><code>}</code></p>
     <p><code>n=0</code></p>
     <p><code>ack(3,3)</code></p>
     <p><code>print n, "calls\n"</code></p>
    </section>
    <section>
     <title>
      <p>3.7.2 <code>ack1</code></p>
     </title>
     <p><code>func ack() {</code></p>
     <p><code> n = n+1</code></p>
     <p><code> if ($1 == 0) return ($2+1)</code></p>
     <p><code> if ($2 == 0) return (ack($1 - 1, 1))</code></p>
     <p><code> return (ack($1 - 1, ack($1, $2 - 1)))</code></p>
     <p><code>}</code></p>
     <p><code>n=0</code></p>
     <p><code>while (read(x)) {</code></p>
     <p><code> read(y)</code></p>
     <p><code> print ack(x,y), "\n"</code></p>
     <p><code>}</code></p>
     <p><code>print n,"\n"</code></p>
    </section>
    <section>
     <title>
      <p>3.7.3 <code>code.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <empty-line/>
     <p><code>#define NSTACK 256</code></p>
     <p><code>static Datum stack[NSTACK]; /* the stack */</code></p>
     <p><code>static Datum *stackp; /* next free spot on stack */</code></p>
     <empty-line/>
     <p><code>#define NPROG 2000</code></p>
     <p><code>Inst prog[NPROG]; /* the machine */</code></p>
     <p><code>Inst *progp; /* next free spot for code generation */</code></p>
     <p><code>Inst *pc; /* program counter during execution */</code></p>
     <p><code>Inst *progbase = prog; /* start of current subprogram */</code></p>
     <p><code>int  returning; /* 1 if return stmt seen */</code></p>
     <empty-line/>
     <p><code>typedef struct Frame { /* proc/func call stack frame */</code></p>
     <p><code> Symbol *sp;    /* symbol table entry */</code></p>
     <p><code> Inst   *retpc; /* where to resume after return */</code></p>
     <p><code> Datum  *argn;  /* n-th argument on stack */</code></p>
     <p><code> int    nargs;  /* number of arguments */</code></p>
     <p><code>} Frame;</code></p>
     <empty-line/>
     <p><code>#define NFRAME 100</code></p>
     <p><code>Frame frame[NFRAME];</code></p>
     <p><code>Frame *fp; /* frame pointer */</code></p>
     <empty-line/>
     <p><code>initcode() {</code></p>
     <p><code> progp = progbase;</code></p>
     <p><code> stackp = stack;</code></p>
     <p><code> fp = frame;</code></p>
     <p><code> returning = 0;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>push(d)</code></p>
     <p><code> Datum d;</code></p>
     <p><code>{</code></p>
     <p><code> if (stackp &gt;= &amp;stack[NSTACK])</code></p>
     <p><code>  execerror("stack too deep", (char*)0);</code></p>
     <p><code> *stackp++ = d;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>Datum pop() {</code></p>
     <p><code> if (stackp == stack)</code></p>
     <p><code>  execerror("stack underflow", (char*)0);</code></p>
     <p><code> return *--stackp;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>constpush() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d.val = ((Symbol*)*pc++)-&gt;u.val;</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>varpush() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> d.sym = (Symbol*)(*pc++);</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>whilecode() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> Inst *savepc = pc;</code></p>
     <empty-line/>
     <p><code> execute(savepc+2); /* condition */</code></p>
     <p><code> d = pop();</code></p>
     <p><code> while (d.val) {</code></p>
     <p><code>  execute(*((Inst**)(savepc))); /* body */</code></p>
     <p><code>  if (returning)</code></p>
     <p><code>   break;</code></p>
     <p><code>  execute(savepc+2); /* condition */</code></p>
     <p><code>  d = pop();</code></p>
     <p><code> }</code></p>
     <p><code> if (!returning)</code></p>
     <p><code>  pc = *((Inst**)(savepc+1)); /* next stmt */</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>ifcode() {</code></p>
     <p><code> Datum d;</code></p>
     <p><code> Inst *savepc = pc; /* then part */</code></p>
     <empty-line/>
     <p><code> execute(savepc+3); /* condition */</code></p>
     <p><code> d = pop();</code></p>
     <p><code> if (d.val)</code></p>
     <p><code>  execute(*((Inst**)(savepc)));</code></p>
     <p><code> else if (*((Inst**)(savepc+1))) /* else part? */</code></p>
     <p><code>  execute(*((Inst**)(savepc+1)));</code></p>
     <p><code> if (!returning)</code></p>
     <p><code>  pc = *((Inst**)(savepc+2)); /* next stmt */</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>define(sp) /* put func/proc in symbol table */</code></p>
     <p><code> Symbol *sp;</code></p>
     <p><code>{</code></p>
     <p><code> sp-&gt;u.defn = (Inst)progbase; /* start of code */</code></p>
     <p><code> progbase = progp; /* next code starts here */</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>call() /* call a function */</code></p>
     <p><code>{</code></p>
     <p><code> Symbol *sp = (Symbol*)pc[0]; /* symbol table entry */</code></p>
     <empty-line/>
     <p><code> /* for function */</code></p>
     <p><code> if (fp++ &gt;= &amp;frame[NFRAME-1])</code></p>
     <p><code>  execerror(sp-&gt;name, "call nested too deeply");</code></p>
     <p><code> fp-&gt;sp = sp;</code></p>
     <p><code> fp-&gt;nargs = (int)pc[1];</code></p>
     <p><code> fp-&gt;retpc = pc + 2;</code></p>
     <p><code> fp-&gt;argn = stackp - 1; /* last argument */</code></p>
     <p><code> execute(sp-&gt;u.defn);</code></p>
     <p><code> returning = 0;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>ret() /* common return from func or proc */</code></p>
     <p><code>{</code></p>
     <p><code> int i;</code></p>
     <empty-line/>
     <p><code> for (i = 0; i &lt; fp-&gt;nargs; i++)</code></p>
     <p><code>  pop(); /* pop arguments */</code></p>
     <p><code> pc = (Inst*)fp-&gt;retpc;</code></p>
     <p><code> --fp;</code></p>
     <p><code> returning = 1;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>funcret() /* return from a function */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <empty-line/>
     <p><code> if (fp-&gt;sp-&gt;type == PROCEDURE)</code></p>
     <p><code>  execerror(fp-&gt;sp-&gt;name, "(proc) returns value");</code></p>
     <p><code> d = pop(); /* preserve function return value */</code></p>
     <p><code> ret();</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>procret() /* return from a procedure */</code></p>
     <p><code>{</code></p>
     <p><code> if (fp-&gt;sp-&gt;type == FUNCTION)</code></p>
     <p><code>  execerror(fp-&gt;sp-&gt;name, "(func) returns no value");</code></p>
     <p><code> ret();</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double *getarg() /* return pointer to argument */</code></p>
     <p><code>{</code></p>
     <p><code> int nargs = (int)*pc++;</code></p>
     <p><code> if (nargs &gt; fp-&gt;nargs)</code></p>
     <p><code>  execerror(fp-&gt;sp-&gt;name, "not enough arguments");</code></p>
     <p><code> return &amp;fp-&gt;argn[nargs - fp-&gt;nargs].val;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>arg() /* push argument onto stack */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <empty-line/>
     <p><code> d.val = *getarg();</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>argassign() /* store top of stack in argument */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <empty-line/>
     <p><code> d = pop();</code></p>
     <p><code> push(d); /* leave value on stack */</code></p>
     <p><code> *getarg() = d.val;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>bltin() {</code></p>
     <p><code> Datum d;</code></p>
     <empty-line/>
     <p><code> d = pop();</code></p>
     <p><code> d.val = (*(double(*)())*pc++)(d.val);</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>eval() /* evaluate variable on stack */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <empty-line/>
     <p><code> d = pop();</code></p>
     <p><code> if (d.sym-&gt;type != VAR &amp;&amp; d.sym-&gt;type != UNDEF)</code></p>
     <p><code>  execerror("attempt to evaluate non-variable", d.sym-&gt;name);</code></p>
     <p><code> if (d.sym-&gt;type == UNDEF)</code></p>
     <p><code>  execerror("undefined variable", d.sym-&gt;name);</code></p>
     <p><code> d.val = d.sym-&gt;u.val;</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>add() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val += d2.val;</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>sub() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val -= d2.val;</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>mul() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val *= d2.val;</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>div() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> if (d2.val == 0.0)</code></p>
     <p><code>  execerror("division by zero", (char *)0);</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val /= d2.val;</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>negate() {</code></p>
     <p><code> Datum d;</code></p>
     <empty-line/>
     <p><code> d = pop();</code></p>
     <p><code> d.val = -d.val;</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>gt() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val &gt; d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>lt() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val &lt; d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>ge() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val &gt;= d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>le() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val &lt;= d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>eq() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val == d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>ne() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val != d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>and() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val != 0.0 &amp;&amp; d2.val != 0.0);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>or() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = (double)(d1.val != 0.0 || d2.val != 0.0);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>not() {</code></p>
     <p><code> Datum d;</code></p>
     <empty-line/>
     <p><code> d = pop();</code></p>
     <p><code> d.val = (double)(d.val == 0.0);</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>power() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <p><code> extern double Pow();</code></p>
     <empty-line/>
     <p><code> d2 = pop();</code></p>
     <p><code> d1 = pop();</code></p>
     <p><code> d1.val = Pow(d1.val, d2.val);</code></p>
     <p><code> push(d1);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>assign() {</code></p>
     <p><code> Datum d1, d2;</code></p>
     <empty-line/>
     <p><code> d1 = pop();</code></p>
     <p><code> d2 = pop();</code></p>
     <p><code> if (d1.sym-&gt;type != VAR &amp;&amp; d1.sym-&gt;type != UNDEF)</code></p>
     <p><code>  execerror("assignment to non-variable", d1.sym-&gt;name);</code></p>
     <p><code> d1.sym-&gt;u.val = d2.val;</code></p>
     <p><code> d1.sym-&gt;type = VAR;</code></p>
     <p><code> push(d2);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>print() /* pop top value from stack, print it */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <empty-line/>
     <p><code> d = pop();</code></p>
     <p><code> printf("\t%.8g\n", d.val);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>prexpr() /* print numeric value */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <empty-line/>
     <p><code> d = pop();</code></p>
     <p><code> printf("%.8g ", d.val);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>prstr() /* print string value */</code></p>
     <p><code>{</code></p>
     <p><code> printf(%s", (char*)*pc++);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>varread() /* read into variable */</code></p>
     <p><code>{</code></p>
     <p><code> Datum d;</code></p>
     <p><code> extern FILE *fin;</code></p>
     <p><code> Symbol *var = (Symbol*)*pc++;</code></p>
     <empty-line/>
     <p><code>Again:</code></p>
     <p><code> switch (fscanf(fin, "%lf", &amp;var-&gt;u.val)) {</code></p>
     <p><code> case EOF:</code></p>
     <p><code>  if (moreinput())</code></p>
     <p><code>   goto Again;</code></p>
     <p><code>  d.val = var-&gt;u.val = 0.0;</code></p>
     <p><code>  break;</code></p>
     <p><code> case 0:</code></p>
     <p><code>  execerror("non-number read into", var-&gt;name);</code></p>
     <p><code>  break;</code></p>
     <p><code> default:</code></p>
     <p><code>  d.val = 1.0;</code></p>
     <p><code>  break;</code></p>
     <p><code> }</code></p>
     <p><code> var-&gt;type = VAR;</code></p>
     <p><code> push(d);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>Inst *code(f) /* install one instruction or operand */</code></p>
     <p><code> Inst f;</code></p>
     <p><code>{</code></p>
     <p><code> Inst *oprogp = progp;</code></p>
     <empty-line/>
     <p><code> if (progp &gt;= &amp;prog[NPROG])</code></p>
     <p><code>  execerror("program too big", (char*)0);</code></p>
     <p><code> *progp++ = f;</code></p>
     <p><code> return oprogp;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>execute(p)</code></p>
     <p><code> Inst *p;</code></p>
     <p><code>{</code></p>
     <p><code> for (pc = p; *pc != STOP &amp;&amp; !returning; )</code></p>
     <p><code>  (*((++pc)[-1]))();</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.7.4 <code>double</code></p>
     </title>
     <p><code>proc double() {</code></p>
     <p><code> if ($1 &gt; 1) {</code></p>
     <p><code>  double($1/2)</code></p>
     <p><code> }</code></p>
     <p><code> print($1)</code></p>
     <p><code>}</code></p>
     <p><code>double(1024)</code></p>
    </section>
    <section>
     <title>
      <p>3.7.5 <code>fac</code></p>
     </title>
     <p><code>func fac() {</code></p>
     <p><code> if ($1 &lt;= 0) return 1 else return $1 * fac($1-1)</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.7.6 <code>fac1</code></p>
     </title>
     <p><code>func fac() if ($1 &lt;= 0) return 1 else return $1 * fac($1-1)</code></p>
     <p><code>fac(0)</code></p>
     <p><code>fac(7)</code></p>
     <p><code>fac(10)</code></p>
    </section>
    <section>
     <title>
      <p>3.7.7 <code>fac2</code></p>
     </title>
     <p><code>func fac() {</code></p>
     <p><code> if ($1 &lt;= 0) {</code></p>
     <p><code>  return 1</code></p>
     <p><code> }</code></p>
     <p><code> return $1 * fac($1-1)</code></p>
     <p><code>}</code></p>
     <p><code>i=0</code></p>
     <p><code>while(i&lt;=20){</code></p>
     <p><code> print "factorial of ", i, "is ", fac(i), "\n"</code></p>
     <p><code> i=i+1</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.7.8 <code>fib</code></p>
     </title>
     <p><code>proc fib() {</code></p>
     <p><code> a = 0</code></p>
     <p><code> b = 1</code></p>
     <p><code> while (b &lt; $1) {</code></p>
     <p><code>  print b</code></p>
     <p><code>  c = b</code></p>
     <p><code>  b = a+b</code></p>
     <p><code>  a = c</code></p>
     <p><code> }</code></p>
     <p><code> print "\n"</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.7.9 <code>fib2</code></p>
     </title>
     <p><code>{</code></p>
     <p><code> n=0</code></p>
     <p><code> a=0</code></p>
     <p><code> b=1</code></p>
     <p><code> while(b&lt;10000000){</code></p>
     <p><code>  n=n+1</code></p>
     <p><code>  c=b</code></p>
     <p><code>  b=a+b</code></p>
     <p><code>  a=c</code></p>
     <p><code>  print(b)</code></p>
     <p><code> }</code></p>
     <p><code> print(n)</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.7.10 <code>fibsum</code></p>
     </title>
     <p><code>proc fib(){</code></p>
     <p><code> a=1</code></p>
     <p><code> b=1</code></p>
     <p><code> c=2</code></p>
     <p><code> d=3</code></p>
     <p><code> sum = a+b+c+d</code></p>
     <p><code> while(d&lt;$1){</code></p>
     <p><code>  e=d+c</code></p>
     <p><code>  print(e)</code></p>
     <p><code>  a=b</code></p>
     <p><code>  b=c</code></p>
     <p><code>  c=d</code></p>
     <p><code>  d=e</code></p>
     <p><code>  sum=sum+e</code></p>
     <p><code> }</code></p>
     <p><code> print(sum)</code></p>
     <p><code>}</code></p>
     <p><code>fib(1000)</code></p>
    </section>
    <section>
     <title>
      <p>3.7.11 <code>fibtest</code></p>
     </title>
     <p><code>proc fib() {</code></p>
     <p><code> a = 0</code></p>
     <p><code> b = 1</code></p>
     <p><code> while (b &lt; $1) {</code></p>
     <p><code>  c = b</code></p>
     <p><code>  b = a+b</code></p>
     <p><code>  a = c</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
     <p><code>i = 1</code></p>
     <p><code>while (i &lt; 1000) {</code></p>
     <p><code> fib(1000)</code></p>
     <p><code> i = i + 1</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.7.12 <code>hoc.h</code></p>
     </title>
     <p><code>typedef struct Symbol { /* symbol table entry */</code></p>
     <p><code> char *name;</code></p>
     <p><code> short type;</code></p>
     <p><code> union {</code></p>
     <p><code>  double val; /* VAR */</code></p>
     <p><code>  double (*ptr)(); /* BLTIN */</code></p>
     <p><code>  int (*defn)(); /* FUNCTION, PROCEDURE */</code></p>
     <p><code>  char *str; /* STRING */</code></p>
     <p><code> } u;</code></p>
     <p><code> struct Symbol *next; /* to link to another */</code></p>
     <p><code>} Symbol;</code></p>
     <p><code>Symbol *install(), *lookup();</code></p>
     <empty-line/>
     <p><code>typedef union Datum { /* interpreter stack type */</code></p>
     <p><code> double val;</code></p>
     <p><code> Symbol *sym;</code></p>
     <p><code>} Datum;</code></p>
     <p><code>extern Datum pop();</code></p>
     <p><code>extern eval(), add(), sub(), mul(), div(), negate(), power();</code></p>
     <empty-line/>
     <p><code>typedef int (*Inst)();</code></p>
     <p><code>#define STOP (Inst)0</code></p>
     <empty-line/>
     <p><code>extern Inst *progp, *progbase, prog[], *code();</code></p>
     <p><code>extern assign(), bltin(), varpush(), constpush(), print(), varread();</code></p>
     <p><code>extern prexpr(), prstr();</code></p>
     <p><code>extern gt(), lt(), eq(), ge(), le(), ne(), and(), or(), not();</code></p>
     <p><code>extern ifcode(), whilecode(), call(), arg(), argassign();</code></p>
     <p><code>extern funcret(), procret();</code></p>
    </section>
    <section>
     <title>
      <p>3.7.13 <code>hoc.ms</code></p>
     </title>
     <p><code>.EQ</code></p>
     <p><code>delim @@</code></p>
     <p><code>.EN</code></p>
     <p><code>.TL</code></p>
     <p><code>Hoc - An Interactive Language For Floating Point Arithmetic</code></p>
     <p><code>.AU</code></p>
     <p><code>Brian Kernighan</code></p>
     <p><code>Rob Pike</code></p>
     <p><code>.AB</code></p>
     <p><code>.I Hoc</code></p>
     <p><code>is a simple programmable interpreter</code></p>
     <p><code>for floating point expressions.</code></p>
     <p><code>It has C-style control flow,</code></p>
     <p><code>function definition and the usual</code></p>
     <p><code>numerical built-in functions such as cosine and logarithm.</code></p>
     <p><code>.AE</code></p>
     <p><code>.NH</code></p>
     <p><code>Expressions</code></p>
     <p><code>.PP</code></p>
     <p><code>.I Hoc</code></p>
     <p><code>is an expression language,</code></p>
     <p><code>much like C:</code></p>
     <p><code>although there are several control-flow statements,</code></p>
     <p><code>most statements such as assignments</code></p>
     <p><code>are expressions whose value is disregarded.</code></p>
     <p><code>For example, the assignment operator</code></p>
     <p><code>= assigns the value of its right operand</code></p>
     <p><code>to its left operand, and yields the value,</code></p>
     <p><code>so multiple assignments work.</code></p>
     <p><code>The expression grammar is:</code></p>
     <p><code>.DS</code></p>
     <p><code>.I</code></p>
     <p><code>expr: number</code></p>
     <p><code> | variable</code></p>
     <p><code> | ( expr )</code></p>
     <p><code> | expr binop expr</code></p>
     <p><code> | unop expr</code></p>
     <p><code> | function ( arguments )</code></p>
     <p><code>.R</code></p>
     <p><code>.DE</code></p>
     <p><code>Numbers are floating point.</code></p>
     <p><code>The input format is</code></p>
     <p><code>that recognized by @scanf@(3):</code></p>
     <p><code>.ix [scanf]</code></p>
     <p><code>digits, decimal point, digits,</code></p>
     <p><code>.ix [hoc] manual</code></p>
     <p><code>.ix assignment expression</code></p>
     <p><code>.ix multiple assignment</code></p>
     <p><code>@e@ or @E@, signed exponent.</code></p>
     <p><code>At least one digit or a decimal point</code></p>
     <p><code>must be present;</code></p>
     <p><code>the other components are optional.</code></p>
     <p><code>.PP</code></p>
     <p><code>Variable names are formed from a letter</code></p>
     <p><code>followed by a string of letters and numbers,</code></p>
     <p><code>@binop@ refers to binary operators such</code></p>
     <p><code>as addition or logical comparison;</code></p>
     <p><code>@unop@ refers to the two negation operators,</code></p>
     <p><code>'!' (logical negation, 'not')</code></p>
     <p><code>and '\-' (arithmetic negation, sign change).</code></p>
     <p><code>Table 1 lists the operators.</code></p>
     <p><code>.TS</code></p>
     <p><code>center, box;</code></p>
     <p><code>с s</code></p>
     <p><code>lfCW l.</code></p>
     <p><code>\fBTable 1:\fP Operators, in decreasing order of precedence</code></p>
     <p><code>.sp .5</code></p>
     <p><code>^       exponentiation (\s-1FORTRAN\s0 **), right associative</code></p>
     <p><code>! \-    (unary) logical and arithmetic negation</code></p>
     <p><code>* /     multiplication, division</code></p>
     <p><code>+ \-    addition, subtraction</code></p>
     <p><code>&gt; &gt;=    relational operators: greater, greater or equal,</code></p>
     <p><code>&lt; &lt;=    less, less or equal,</code></p>
     <p><code>\&amp;== != equal, not equal (all same precedence)</code></p>
     <p><code>&amp;&amp;      logical AND (both operands always evaluated)</code></p>
     <p><code>||      logical OR (both operands always evaluated)</code></p>
     <p><code>\&amp;=     assignment, right associative</code></p>
     <p><code>.ТЕ</code></p>
     <p><code>.ix table~of [hoc] operators</code></p>
     <p><code>.PP</code></p>
     <p><code>Functions, as described later, may be defined by the user.</code></p>
     <p><code>Function arguments are expressions separated by commas.</code></p>
     <p><code>There are also a number of built-in functions,</code></p>
     <p><code>all of which take a single argument,</code></p>
     <p><code>described in Table 2.</code></p>
     <p><code>.TS</code></p>
     <p><code>center, box;</code></p>
     <p><code>с s</code></p>
     <p><code>lfCW l.</code></p>
     <p><code>\fBTable 2:\fP Built-in Functions</code></p>
     <p><code>.sp .5</code></p>
     <p><code>abs(x)   @| x |@, absolute value of @x@</code></p>
     <p><code>atan(x)  arc tangent of @x@</code></p>
     <p><code>cos(x)   @cos (x)@, cosine of @x@</code></p>
     <p><code>exp(x)   @e sup x@, exponential of @x@</code></p>
     <p><code>int(x)   integer part of @x@, truncated towards zero</code></p>
     <p><code>log(x)   @log (x)@, logarithm base @e@ of @x@</code></p>
     <p><code>log10(x) @log sub 10 (x)@, logarithm base 10 of @x@</code></p>
     <p><code>sin(x)   @sin (x)@, sine of @x@</code></p>
     <p><code>sqrt(x)  @sqrt x@, @x sup half@</code></p>
     <p><code>.ТЕ</code></p>
     <p><code>.ix table~of [hoc] functions</code></p>
     <p><code>.PP</code></p>
     <p><code>Logical expressions have value 1.0 (true) and 0.0 (false).</code></p>
     <p><code>As in C,</code></p>
     <p><code>any non-zero value is taken to be true.</code></p>
     <p><code>As is always the case with floating point numbers,</code></p>
     <p><code>equality comparisons are inherently suspect. .PP</code></p>
     <p><code>.I Hoc</code></p>
     <p><code>also has a few built-in constants, shown in Table 3.</code></p>
     <p><code>.TS</code></p>
     <p><code>center, box;</code></p>
     <p><code>c s s</code></p>
     <p><code>lfCW n l.</code></p>
     <p><code>\fBTable 3:\fP Built-in Constants</code></p>
     <p><code>.sp .5</code></p>
     <p><code>DEG   57.29577951308232087680 @180/ pi@, degrees per radian</code></p>
     <p><code>E     2.71828182845904523536  @e@, base of natural logarithms</code></p>
     <p><code>GAMMA 0.57721566490153286060  @gamma@, Euler-Mascheroni constant</code></p>
     <p><code>PHI   1.61803398874989484820  @( sqrt 5 +1)/2@, the golden ratio</code></p>
     <p><code>PI    3.14159265358979323846  @pi@, circular transcendental number</code></p>
     <p><code>.ТЕ</code></p>
     <p><code>.ix table~of [hoc] constants</code></p>
     <p><code>.NH</code></p>
     <p><code>Statements and Control Flow</code></p>
     <p><code>.PP</code></p>
     <p><code>.I Hoc</code></p>
     <p><code>statements have the following grammar:</code></p>
     <p><code>.DS</code></p>
     <p><code>.I</code></p>
     <p><code>stmt: expr</code></p>
     <p><code> | variable = expr</code></p>
     <p><code> | procedure ( arglist )</code></p>
     <p><code> | while ( expr ) stmt</code></p>
     <p><code> | if ( expr ) stmt</code></p>
     <p><code> | if ( expr ) stmt else stmt</code></p>
     <p><code> | { stmtlist }</code></p>
     <p><code> | print expr-list</code></p>
     <p><code> | return optional-expr</code></p>
     <empty-line/>
     <p><code>stmtlist: \fR(nothing)\fI</code></p>
     <p><code> | stmlist stmt</code></p>
     <p><code>.R</code></p>
     <p><code>.DE</code></p>
     <p><code>An assignment is parsed by default as a statement rather than</code></p>
     <p><code>an expression, so assignments typed interactively</code></p>
     <p><code>do not print their value.</code></p>
     <p><code>.PP</code></p>
     <p><code>Note that semicolons are not special to</code></p>
     <p><code>.ix [hoc] input~format</code></p>
     <p><code>@hoc@: statements are terminated by newlines.</code></p>
     <p><code>This causes some peculiar behavior.</code></p>
     <p><code>The following are legal</code></p>
     <p><code>.IT if</code></p>
     <p><code>statements:</code></p>
     <p><code>.DS</code></p>
     <p><code>.ft CW</code></p>
     <p><code>if (x &lt; 0) print(y) else print(z)</code></p>
     <empty-line/>
     <p><code>if (x &lt; 0) {</code></p>
     <p><code> print(y)</code></p>
     <p><code>} else {</code></p>
     <p><code> print(z)</code></p>
     <p><code>}</code></p>
     <p><code>.ft</code></p>
     <p><code>.DE</code></p>
     <p><code>In the second example, the braces are mandatory:</code></p>
     <p><code>the newline after the</code></p>
     <p><code>.I if</code></p>
     <p><code>would terminate the statement and produce a syntax error were</code></p>
     <p><code>the brace omitted.</code></p>
     <p><code>.PP</code></p>
     <p><code>The syntax and semantics of @hoc@</code></p>
     <p><code>control flow facilities are basically the same as in C.</code></p>
     <p><code>The</code></p>
     <p><code>.I while</code></p>
     <p><code>and</code></p>
     <p><code>.I if</code></p>
     <p><code>statements are just as in C, except there are no @break@ or</code></p>
     <p><code>@continue@ statements.</code></p>
     <p><code>.NH</code></p>
     <p><code>Input and Output: @read@ and @print@</code></p>
     <p><code>.PP</code></p>
     <p><code>.ix [hoc] [read]~statement</code></p>
     <p><code>.ix [hoc] [print]~statement</code></p>
     <p><code>The input function @read@, like the other built-ins,</code></p>
     <p><code>takes a single argument. Unlike the built-ins, though, the argument</code></p>
     <p><code>is not ал expression: it is the name of a variable.</code></p>
     <p><code>The next number (as defined above) is read from the standard input</code></p>
     <p><code>and assigned to the named variable.</code></p>
     <p><code>The return value of @read@ is 1 (true) if a value was read, and 0 (false)</code></p>
     <p><code>if @read@ encountered end of file or an error.</code></p>
     <p><code>.PP</code></p>
     <p><code>Output is generated with the ©print© statement.</code></p>
     <p><code>The arguments to @print@ are a comma-separated list of expressions</code></p>
     <p><code>and strings in double quotes, as in C.</code></p>
     <p><code>Newlines must be supplied;</code></p>
     <p><code>they are never provided automatically by @print@.</code></p>
     <p><code>.PP</code></p>
     <p><code>Note that @read@ is a special built-in function, and therefore takes</code></p>
     <p><code>a single parenthesized argument, while @print@ is a statement that takes</code></p>
     <p><code>a comma-separated, unparenthesized list:</code></p>
     <p><code>.DS</code></p>
     <p><code>.ft CW</code></p>
     <p><code>while (read(x)) {</code></p>
     <p><code> print "value is ", x, "\n"</code></p>
     <p><code>}</code></p>
     <p><code>.ft</code></p>
     <p><code>.DE</code></p>
     <p><code>.NH</code></p>
     <p><code>Functions and Procedures</code></p>
     <p><code>.PP</code></p>
     <p><code>Functions and procedures are distinct in @hoc@,</code></p>
     <p><code>although they are defined by the same mechanism.</code></p>
     <p><code>This distinction is simply for run-time error checking:</code></p>
     <p><code>it is an error for a procedure to return a value,</code></p>
     <p><code>and for a function @not@ to return one.</code></p>
     <p><code>.PP</code></p>
     <p><code>The definition syntax is:</code></p>
     <p><code>.ix [hoc] function~definition</code></p>
     <p><code>.ix [hoc] procedure~definition</code></p>
     <p><code>.DS</code></p>
     <p><code>.I</code></p>
     <p><code>.ta 1i</code></p>
     <p><code>function: func name() stmt</code></p>
     <empty-line/>
     <p><code>procedure: proc name() stmt</code></p>
     <p><code>.R</code></p>
     <p><code>.DE</code></p>
     <p><code>.I name</code></p>
     <p><code>may be the name of any variable \(em built-in functions are excluded.</code></p>
     <p><code>The definition, up to the opening brace or statement,</code></p>
     <p><code>must be on one line, as with the</code></p>
     <p><code>.I if</code></p>
     <p><code>statements above.</code></p>
     <p><code>.PP</code></p>
     <p><code>Unlike C,</code></p>
     <p><code>the body of a function or procedure may be any statement, not</code></p>
     <p><code>necessarily a compound (brace-enclosed) statement.</code></p>
     <p><code>Since semicolons have no meaning in @hoc@,</code></p>
     <p><code>a null procedure body is formed by an empty pair of braces.</code></p>
     <p><code>.PP</code></p>
     <p><code>Functions and procedures may take arguments, separated by commas,</code></p>
     <p><code>when invoked. Arguments are referred to as in the shell:</code></p>
     <p><code>.ix [hoc] arguments</code></p>
     <p><code>.IT $3</code></p>
     <p><code>refers to the third (1-indexed) argument.</code></p>
     <p><code>They are passed by value and within functions</code></p>
     <p><code>are semantically equivalent to variables.</code></p>
     <p><code>It is an error to refer to an argument numbered greater than the</code></p>
     <p><code>number of arguments passed to the routine. The error checking</code></p>
     <p><code>is done dynamically, however, so a routine may have variable numbers</code></p>
     <p><code>of arguments if initial arguments affect the number of arguments</code></p>
     <p><code>to be referenced (as in C's @printf@).</code></p>
     <p><code>.PP</code></p>
     <p><code>Functions and procedures may recurse, but the stack has limited depth</code></p>
     <p><code>(about a hundred calls). The following shows a</code></p>
     <p><code>.I</code></p>
     <p><code>hoc</code></p>
     <p><code>definition of Ackermann's function:</code></p>
     <p><code>.ix Ackermann's~function</code></p>
     <p><code>.DS</code></p>
     <p><code>.ft CW</code></p>
     <p><code>.ix [ack]~function</code></p>
     <p><code>.S $ "hoc</code></p>
     <p><code>.S "func ack() {</code></p>
     <p><code>.S " if ($1 == 0) return $2+1</code></p>
     <p><code>.S " if ($2 == 0) return ack($1-1, 1)</code></p>
     <p><code>.S " return ack($1-1, ack($1, $2-1))</code></p>
     <p><code>.S "}</code></p>
     <p><code>.S "ack(3, 2)</code></p>
     <p><code> 29</code></p>
     <p><code>.S "ack(3, 3)</code></p>
     <p><code> 61</code></p>
     <p><code>.S "ack(3, 4)</code></p>
     <p><code>hoc: stack too deep near line 8</code></p>
     <p><code>\&amp;...</code></p>
     <p><code>.ft</code></p>
     <p><code>.DE</code></p>
     <p><code>.bp</code></p>
     <p><code>.NH</code></p>
     <p><code>Examples</code></p>
     <p><code>.PP</code></p>
     <p><code>Stirling's~formula:</code></p>
     <p><code>.ix Stirling's~formula</code></p>
     <p><code>.EQ</code></p>
     <p><code>n! ~\(ap~ sqrt {2n pi} (n/e) sup n (1+ 1 over 12n )</code></p>
     <p><code>.EN</code></p>
     <p><code>.DS</code></p>
     <p><code>.ft CW</code></p>
     <p><code>.S $ hoc</code></p>
     <p><code>.S "func stirl() {</code></p>
     <p><code>.S " return sqrt(2*$1*PI) * ($1/E)"$1*(1 + 1/(12*$1)) .S "}</code></p>
     <p><code>.S "stirl(10)</code></p>
     <p><code> 3628684.7</code></p>
     <p><code>.S stirl(20)</code></p>
     <p><code> 2.4328818e+18</code></p>
     <p><code>.ft R</code></p>
     <p><code>.DE</code></p>
     <p><code>.PP</code></p>
     <p><code>Factorial function, @n!@:</code></p>
     <p><code>.ix [fac]~function</code></p>
     <p><code>.DS</code></p>
     <p><code>. S "func fac() if ($1 &lt;= 0) return 1 else return $1 * fac($1-1)</code></p>
     <p><code>.ft R</code></p>
     <p><code>.DE</code></p>
     <p><code>.PP</code></p>
     <p><code>Ratio of factorial to Stirling approximation:</code></p>
     <p><code>.DS</code></p>
     <p><code>.S "i = 9</code></p>
     <p><code>.S "while ((i = i+1) &lt;= 20) {</code></p>
     <p><code>.S \ \ \ \ \ \ \ \ print\ i,\ "\ \ ",\ fac(i)/stirl(i),\ "\en"</code></p>
     <p><code>.S "} .ft CW</code></p>
     <p><code>10 1.0000318</code></p>
     <p><code>11 1.0000265</code></p>
     <p><code>12 1.0000224</code></p>
     <p><code>13 1.0000192</code></p>
     <p><code>14 1.0000166</code></p>
     <p><code>15 1.0000146</code></p>
     <p><code>16 1.0000128</code></p>
     <p><code>17 1.0000114</code></p>
     <p><code>18 1.0000102</code></p>
     <p><code>19 1.0000092</code></p>
     <p><code>20 1.0000083</code></p>
     <p><code>.ft</code></p>
     <p><code>.DE</code></p>
    </section>
    <section>
     <title>
      <p>3.7.14 <code>hoc.y</code></p>
     </title>
     <p><code>%{</code></p>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#define code2(c1,c2) code(c1); code(c2)</code></p>
     <p><code>#define code3(c1,c2,c3) code(c1); code(c2); code(c3)</code></p>
     <p><code>%}</code></p>
     <p><code>%union {</code></p>
     <p><code> Symbol *sym; /* symbol table pointer */</code></p>
     <p><code> Inst *inst; /* machine instruction */</code></p>
     <p><code> int narg; /* number of arguments */</code></p>
     <p><code>}</code></p>
     <p><code>%token &lt;sym&gt; NUMBER STRING PRINT VAR BLTIN UNDEF WHILE IF ELSE</code></p>
     <p><code>%token &lt;sym&gt; FUNCTION PROCEDURE RETURN FUNC PROC READ</code></p>
     <p><code>%token &lt;narg&gt; ARG</code></p>
     <p><code>%type &lt;inst&gt; expr stmt asgn prlist stmtlist</code></p>
     <p><code>%type &lt;inst&gt; cond while if begin end</code></p>
     <p><code>%type &lt;sym&gt; procname</code></p>
     <p><code>%type &lt;narg&gt; arglist</code></p>
     <p><code>%right '='</code></p>
     <p><code>%left OR</code></p>
     <p><code>%left AND</code></p>
     <p><code>%left GT GE LT LE EQ NE</code></p>
     <p><code>%left '+' '-' %left '/'</code></p>
     <p><code>%left UNARYMINUS NOT</code></p>
     <p><code>%right '^'</code></p>
     <p><code>%%</code></p>
     <p><code>list: /* nothing */</code></p>
     <p><code> | list '\n'</code></p>
     <p><code> | list defn '\n'</code></p>
     <p><code> | list asgn '\n' { code2(pop, STOP); return 1; }</code></p>
     <p><code> | list stmt '\n' { code(STOP); return 1; }</code></p>
     <p><code> | list expr '\n' { code2(print, STOP); return 1; }</code></p>
     <p><code> | list error '\n' { yyerrok; }</code></p>
     <p><code> ;</code></p>
     <p><code>asgn: VAR '=' expr { code3(varpush,(Inst)$1,assign); $$=$3; }</code></p>
     <p><code> | ARG '=' expr</code></p>
     <p><code> { defnonly("$"); code2(argassign,(Inst)$1); $$=$3;}</code></p>
     <p><code> ;</code></p>
     <p><code>stmt: expr { code(pop); }</code></p>
     <p><code> | RETURN { defnonly("return"); code(procret); }</code></p>
     <p><code> | RETURN expr</code></p>
     <p><code> { defnonly("return"); $$=$2; code(funcret); }</code></p>
     <p><code> | PROCEDURE begin '(' arglist ')'</code></p>
     <p><code> { $$ = $2; code3(call, (Inst)$1, (Inst)$4); }</code></p>
     <p><code> | PRINT prlist { $$ = $2; }</code></p>
     <p><code> | while cond stmt end {</code></p>
     <p><code>  ($1)UID = (Inst)$3; /* body of loop */</code></p>
     <p><code>  ($1)[2] = (Inst)$4;</code></p>
     <p><code> } /* end, if cond fails */</code></p>
     <p><code> | if cond stmt end { /* else-less if */</code></p>
     <p><code>  ($1)[1] = (Inst)$3; /* thenpart */</code></p>
     <p><code>  ($1)[3] = (Inst)$4;</code></p>
     <p><code> } /* end, if cond fails */</code></p>
     <p><code> | if cond stmt end ELSE stmt end { /* if with else */</code></p>
     <p><code>  ($1)[1] = (Inst)$3; /* thenpart */</code></p>
     <p><code>  ($1)[2] = (Inst)$6; /* elsepart */</code></p>
     <p><code>  ($1)[3] = (Inst)$7;</code></p>
     <p><code> } /* end, if cond fails */</code></p>
     <p><code> | '{' stmtlist '}' { $$ = $2; }</code></p>
     <p><code> ;</code></p>
     <p><code>cond: '(' expr ')' { code(STOP); $$ = $2; }</code></p>
     <p><code> ;</code></p>
     <p><code>while: WHILE { $$ = code3(whilecode,STOP,STOP); }</code></p>
     <p><code> ;</code></p>
     <p><code>if: IF { $$ = code(ifcode); code3(STOP,STOP,STOP); }</code></p>
     <p><code> ;</code></p>
     <p><code>begin: /* nothing */ { $$ = progp; }</code></p>
     <p><code> ;</code></p>
     <p><code>end: /* nothing */ { code(STOP); $$ = progp; }</code></p>
     <p><code> ;</code></p>
     <p><code>stmtlist: /* nothing */ { $$ = progp; }</code></p>
     <p><code> | stmtlist '\n'</code></p>
     <p><code> | stmtlist stmt</code></p>
     <p><code> ;</code></p>
     <p><code>expr: NUMBER { $$ = code2(constpush, (Inst)$1); }</code></p>
     <p><code> | VAR { $$ = code3(varpush, (Inst)$1, eval); }</code></p>
     <p><code> | ARG { defnonly("$"); $$ = code2(arg, (Inst)$1); }</code></p>
     <p><code> | asgn</code></p>
     <p><code> | FUNCTION begin '(' arglist ');</code></p>
     <p><code> { $$ = $2; code3(call,(Inst)$1,(Inst)$4); }</code></p>
     <p><code> | READ '(' VAR ')'{$$ = code2(varread, (Inst)$3); }</code></p>
     <p><code> | BLTIN '(' expr ')' { $$=$3; code2(bltin, (Inst)$1-&gt;u.ptr); }</code></p>
     <p><code> | '(' expr ')' { $$ = $2; }</code></p>
     <p><code> | expr '+' expr { code(add); }</code></p>
     <p><code> | expr '-' expr { code(sub); }</code></p>
     <p><code> | expr '*' expr { code(mul); }</code></p>
     <p><code> | expr '/' expr { code(div); }</code></p>
     <p><code> | expr '^' expr { code(power); }</code></p>
     <p><code> | '-' expr %prec UNARYMINUS { $$=$2; code(negate); }</code></p>
     <p><code> | expr GT expr { code(gt); }</code></p>
     <p><code> | expr GE expr { code(ge); }</code></p>
     <p><code> | expr LT expr { code(lt); }</code></p>
     <p><code> | expr LE expr { code(le); }</code></p>
     <p><code> | expr EQ expr { code(eq); }</code></p>
     <p><code> | expr NE expr { code(ne); }</code></p>
     <p><code> | expr AND expr { code(and); }</code></p>
     <p><code> | expr OR expr { code(or); }</code></p>
     <p><code> | NOT expr { $$ = $2; code(not); }</code></p>
     <p><code> ;</code></p>
     <p><code>prlist: expr { code(prexpr); }</code></p>
     <p><code> | STRING { $$ = code2(prstr, (Inst)$1); }</code></p>
     <p><code> | prlist expr { code(prexpr); }</code></p>
     <p><code> | prlist STRING { code2(prstr, (Inst)$3); }</code></p>
     <p><code> ;</code></p>
     <p><code>defn: FUNC procname { $2-&gt;type=FUNCTION; indef=1; }</code></p>
     <p><code> '(' ')' stmt { code(procret); define($2); indef=0; }</code></p>
     <p><code> | PROC procname { $2-&gt;type=PROCEDURE; indef=1; }</code></p>
     <p><code> '(' ')' stmt { code(procret); define($2); indef=0; }</code></p>
     <p><code> ;</code></p>
     <p><code>procname: VAR</code></p>
     <p><code> | FUNCTION</code></p>
     <p><code> | PROCEDURE</code></p>
     <p><code> ;</code></p>
     <p><code>arglist: /* nothing */ { $$ = 0; }</code></p>
     <p><code> | expr { $$ = 1; }</code></p>
     <p><code> | arglist expr { $$ = $1 + 1; }</code></p>
     <p><code> ;</code></p>
     <p><code>%%</code></p>
     <p><code>/* end of grammar */</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;ctype.h&gt;</code></p>
     <p><code>char *progname;</code></p>
     <p><code>int lineno = 1;</code></p>
     <p><code>#include &lt;signal.h&gt;</code></p>
     <p><code>#include &lt;setjmp.h&gt;</code></p>
     <empty-line/>
     <p><code>jmp_buf begin;</code></p>
     <p><code>int indef;</code></p>
     <p><code>char *infile; /* input file name */</code></p>
     <p><code>FILE *fin; /* input file pointer */</code></p>
     <p><code>char **gargv; /* global argument list */</code></p>
     <p><code>int gargc;</code></p>
     <empty-line/>
     <p><code>int c; /* global for use by warning() */</code></p>
     <empty-line/>
     <p><code>yylex() /* hoc6 */</code></p>
     <p><code>{</code></p>
     <p><code> while ((c=getc(fin)) == ' ' || c == '\t')</code></p>
     <p><code>  ;</code></p>
     <p><code> if (c == EOF)</code></p>
     <p><code>  return 0;</code></p>
     <p><code> if (c == '.' || isdigit(c)) { /* number */</code></p>
     <p><code>  double d;</code></p>
     <p><code>  ungetc(c, fin);</code></p>
     <p><code>  fscanf(fin, "%lf", &amp;d);</code></p>
     <p><code>  yylval.sym = install("", NUMBER, d);</code></p>
     <p><code>  return NUMBER;</code></p>
     <p><code> }</code></p>
     <p><code> if (isalpha(c)) {</code></p>
     <p><code>  Symbol *s;</code></p>
     <p><code>  char sbuf[100], *p = sbuf;</code></p>
     <p><code>  do {</code></p>
     <p><code>   if (p &gt;= sbuf + sizeof(sbuf) - 1) {</code></p>
     <p><code>    *p = '\0';</code></p>
     <p><code>    execerror("name too long", sbuf);</code></p>
     <p><code>   }</code></p>
     <p><code>   *p++ = c;</code></p>
     <p><code>  } while ((c=getc(fin)) != EOF &amp;&amp; isalnum(c));</code></p>
     <p><code>  ungetc(c, fin);</code></p>
     <p><code>  *p = '\0';</code></p>
     <p><code>  if ((s=lookup(sbuf)) == 0)</code></p>
     <p><code>   s = install(sbuf, UNDEF, 0.0);</code></p>
     <p><code>  yylval.sym = s;</code></p>
     <p><code>  return s-&gt;type == UNDEF ? VAR : s-&gt;type;</code></p>
     <p><code> }</code></p>
     <p><code> if (c == '$') { /* argument? */</code></p>
     <p><code>  int n = 0;</code></p>
     <p><code>  while (isdigit(c=getc(fin)))</code></p>
     <p><code>   n=10*n+c- '0';</code></p>
     <p><code>  ungetc(c, fin);</code></p>
     <p><code>  if (n == 0)</code></p>
     <p><code>   execerror("strange $...", (char*)0);</code></p>
     <p><code>  yylval.narg = n;</code></p>
     <p><code>  return ARG;</code></p>
     <p><code> }</code></p>
     <p><code> if (c == '"') { /* quoted string */</code></p>
     <p><code>  char sbuf[100], *p, *emalloc();</code></p>
     <p><code>  for (p = sbuf; (c=getc(fin)) != '"'; p++) {</code></p>
     <p><code>   if (с == '\n' || c == EOF)</code></p>
     <p><code>    execerror("missing quote", "");</code></p>
     <p><code>   if (p &gt;= sbuf + sizeof(sbuf) - 1) {</code></p>
     <p><code>    *p = '\0';</code></p>
     <p><code>    execerror("string too long", sbuf);</code></p>
     <p><code>   }</code></p>
     <p><code>   *p = backslash(c);</code></p>
     <p><code>  }</code></p>
     <p><code>  *p = 0;</code></p>
     <p><code>  yylval.sym = (Symbol*)emalloc(strlen(sbuf)+1);</code></p>
     <p><code>  strcpy(yylval.sym, sbuf);</code></p>
     <p><code>  return STRING;</code></p>
     <p><code> }</code></p>
     <p><code> switch (c) {</code></p>
     <p><code> case '&gt;': return follow('=', GE, GT);</code></p>
     <p><code> case '&lt;': return follow('=', LE, LT);</code></p>
     <p><code> case '=': return follow('=', EQ, '=');</code></p>
     <p><code> case '!': return follow('=', NE, NOT);</code></p>
     <p><code> case '|': return follow(' |', OR, '|');</code></p>
     <p><code> case '&amp;': return follow('&amp;', AND, '&amp;');</code></p>
     <p><code> case '\n': lineno++; return '\n';</code></p>
     <p><code> default: return c;</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>backslash(c) /* get next char with \'s interpreted */</code></p>
     <p><code> int c;</code></p>
     <p><code>{</code></p>
     <p><code> char *index(); /* 'strchr()' in some systems */</code></p>
     <p><code> static char transtab[] = "b\bf\fn\nr\rt\t";</code></p>
     <empty-line/>
     <p><code> if (c != '\\')</code></p>
     <p><code>  return c;</code></p>
     <p><code> с = getc(fin);</code></p>
     <p><code> if (islower(c) &amp;&amp; index(transtab, c))</code></p>
     <p><code>  return index(transtab, с)[1];</code></p>
     <p><code> return c;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>follow(expect, ifyes, ifno) /* look ahead for &gt;=, etc. */</code></p>
     <p><code>{</code></p>
     <p><code> int с = getc(fin);</code></p>
     <p><code> if (c == expect)</code></p>
     <p><code>  return ifyes;</code></p>
     <p><code> ungetc(c, fin);</code></p>
     <p><code> return ifno;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>defnonly(s) /* warn if illegal definition */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> if (!indef)</code></p>
     <p><code>  execerror(s, "used outside definition");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>yyerror(s) /* report compile-time error */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, (char *)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>execerror(s, t) /* recover from run-time error */</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> warning(s, t);</code></p>
     <p><code> fseek(fin, 0L, 2); /* flush rest of file */</code></p>
     <p><code> longjmp(begin, 0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>fpecatch() /* catch floating point exceptions */</code></p>
     <p><code>{</code></p>
     <p><code> execerror("floating point exception", (char*)0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>main(argc, argv) /* hoc6 */</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> int i, fpecatch();</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> if (argc == 1) { /* fake an argument list */</code></p>
     <p><code>  static char *stdinonly[] = { "-" };</code></p>
     <p><code>  gargv = stdinonly;</code></p>
     <p><code>  gargc = 1;</code></p>
     <p><code> } else {</code></p>
     <p><code>  gargv = argv+1;</code></p>
     <p><code>  gargc = argc-1;</code></p>
     <p><code> }</code></p>
     <p><code> init();</code></p>
     <p><code> while (moreinput())</code></p>
     <p><code>  run();</code></p>
     <p><code> return 0;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>moreinput() {</code></p>
     <p><code> if (gargc-- &lt;= 0)</code></p>
     <p><code>  return 0;</code></p>
     <p><code> if (fin &amp;&amp; fin != stdin)</code></p>
     <p><code>  fclose(fin);</code></p>
     <p><code> infile = *gargv++;</code></p>
     <p><code> lineno = 1;</code></p>
     <p><code> if (strcmp(infile, "-") == 0) {</code></p>
     <p><code>  fin = stdin;</code></p>
     <p><code>  infile = 0;</code></p>
     <p><code> } else if ((fin=fopen(infile, "r")) == NULL) {</code></p>
     <p><code>  fprintf (stderr, "%s: can't open %s\n", progname, infile);</code></p>
     <p><code>  return moreinput();</code></p>
     <p><code> }</code></p>
     <p><code> return 1;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>run() /* execute until EOF */</code></p>
     <p><code>{</code></p>
     <p><code> setjmp(begin);</code></p>
     <p><code> signal(SIGFPE, fpecatch);</code></p>
     <p><code> for (initcode(); yyparse(); initcode())</code></p>
     <p><code>  execute(progbase);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>warning(s, t) /* print warning message */</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> fprintf(stderr, "%s: %s", progname, s);</code></p>
     <p><code> if (t)</code></p>
     <p><code>  fprintf(stderr, " %s", t);</code></p>
     <p><code> if (infile)</code></p>
     <p><code>  fprintf(stderr, " in %s", infile);</code></p>
     <p><code> fprintf(stderr, " near line %d\n", lineno);</code></p>
     <p><code> while (c != '\n' &amp;&amp; c != EOF)</code></p>
     <p><code>  с = getc(fin); /* flush rest of input line */</code></p>
     <p><code> if (c == '\n')</code></p>
     <p><code>  lineno++;</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.7.15 <code>init.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <p><code>#include &lt;math.h&gt;</code></p>
     <empty-line/>
     <p><code>extern double Log(), Log10(), Sqrt(), Exp(), integer();</code></p>
     <empty-line/>
     <p><code>static struct { /* Keywords */</code></p>
     <p><code> char *name;</code></p>
     <p><code> int kval;</code></p>
     <p><code>} keywords[] = {</code></p>
     <p><code> "proc",   PROC,</code></p>
     <p><code> "func",   FUNC,</code></p>
     <p><code> "return", RETURN,</code></p>
     <p><code> "if",     IF,</code></p>
     <p><code> "else",   ELSE,</code></p>
     <p><code> "while",  WHILE,</code></p>
     <p><code> "print",  PRINT,</code></p>
     <p><code> "read",   READ,</code></p>
     <p><code> 0,        0,</code></p>
     <p><code>};</code></p>
     <empty-line/>
     <p><code>static struct { /* Constants */</code></p>
     <p><code> char *name;</code></p>
     <p><code> double eval;</code></p>
     <p><code>} consts[] = {</code></p>
     <p><code> "PI",    3.14159265358979323846,</code></p>
     <p><code> "E",     2.71828182845904523536,</code></p>
     <p><code> "GAMMA", 0.57721566490153286060, /* Euler */</code></p>
     <p><code> "DEG",  57.29577951308232087680, /* deg/radian */</code></p>
     <p><code> "PHI",   1.61803398874989484820, /* golden ratio */</code></p>
     <p><code> 0,       0</code></p>
     <p><code>};</code></p>
     <empty-line/>
     <p><code>static struct { /* Built-ins */</code></p>
     <p><code> char *name;</code></p>
     <p><code> double (*func)();</code></p>
     <p><code>} builtins[] = {</code></p>
     <p><code> "sin",   sin,</code></p>
     <p><code> "cos",   cos,</code></p>
     <p><code> "atan",  atan,</code></p>
     <p><code> "log",   Log, /* checks range */</code></p>
     <p><code> "log10", Log10, /* checks range */</code></p>
     <p><code> "exp",   Exp, /* checks range */</code></p>
     <p><code> "sqrt",  Sqrt, /* checks range */</code></p>
     <p><code> "int",   integer,</code></p>
     <p><code> "abs",   fabs,</code></p>
     <p><code> 0, 0</code></p>
     <p><code>};</code></p>
     <empty-line/>
     <p><code>init() /* install constants and built-ins in table */</code></p>
     <p><code>{</code></p>
     <p><code> int i;</code></p>
     <p><code> Symbol *s;</code></p>
     <empty-line/>
     <p><code> for (i = 0; keywords[i].name; i++)</code></p>
     <p><code>  install(keywords[i].name, keywords[i].kval, 0.0);</code></p>
     <p><code> for (i = 0; consts[i].name; i++)</code></p>
     <p><code>  install(consts[i].name, VAR, consts[i].eval);</code></p>
     <p><code> for (i = 0; builtins[i].name; i++) {</code></p>
     <p><code>  s = install(builtins[i].name, BLTIN, 0.0);</code></p>
     <p><code>  s-&gt;u.ptr = builtins[i].func;</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.7.16 <code>makeapp</code></p>
     </title>
     <p><code>#!/bin/sh</code></p>
     <empty-line/>
     <p><code>cd hoc6</code></p>
     <p><code>for i in hoc.y hoc.h symbol.c code.c init.c math.c makefile</code></p>
     <p><code>do</code></p>
     <p><code> echo "</code></p>
     <p><code>**** $i ***************************************</code></p>
     <p><code>"</code></p>
     <p><code> sed 's/\\/\\e/g</code></p>
     <p><code> s/^$/.sp .5/' $i |</code></p>
     <p><code> awk '</code></p>
     <p><code>                      { print }</code></p>
     <p><code>  /(^ ;$)|(^})|(^%%)/ { print ".P3" }</code></p>
     <p><code> '</code></p>
     <p><code>done</code></p>
    </section>
    <section>
     <title>
      <p>3.7.17 <code>makefile</code></p>
     </title>
     <p><code>CC = lcc</code></p>
     <p><code>YFLAGS = -d</code></p>
     <p><code>OBJS = hoc.o code.o init.o math.o symbol.o</code></p>
     <empty-line/>
     <p><code>hoc6: $(OBJS)</code></p>
     <p><code>      $(CC) $(CFLAGS) $(OBJS) -lm -o hoc6</code></p>
     <empty-line/>
     <p><code>hoc.o code.o init.o symbol.o: hoc.h</code></p>
     <empty-line/>
     <p><code>code.o init.o symbol.o: x.tab.h</code></p>
     <empty-line/>
     <p><code>x.tab.h: y.tab.h</code></p>
     <p><code>      -cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h</code></p>
     <empty-line/>
     <p><code>pr: hoc.y hoc.h code.c init.c math.c symbol.c</code></p>
     <p><code>      @pr $?</code></p>
     <p><code>      @touch pr</code></p>
     <empty-line/>
     <p><code>clean:</code></p>
     <p><code>      rm -f $(OBJS) [xy].tab.[ch]</code></p>
    </section>
    <section>
     <title>
      <p>3.7.18 <code>math.c</code></p>
     </title>
     <p><code>#include &lt;math.h&gt;</code></p>
     <p><code>#include &lt;errno.h&gt;</code></p>
     <empty-line/>
     <p><code>extern int errno;</code></p>
     <p><code>double errcheck();</code></p>
     <empty-line/>
     <p><code>double Log(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(log(x), "log");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Log10(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(log10(x), "log10");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Sqrt(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(sqrt(x), "sqrt");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Exp(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(exp(x), "exp");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double Pow(x, y)</code></p>
     <p><code> double x, y;</code></p>
     <p><code>{</code></p>
     <p><code> return errcheck(pow(x,y), "exponentiation");</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double integer(x)</code></p>
     <p><code> double x;</code></p>
     <p><code>{</code></p>
     <p><code> return (double)(long)x;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>double errcheck(d, s) /* check result of library call */</code></p>
     <p><code> double d;</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> if (errno == EDOM) {</code></p>
     <p><code>  errno = 0;</code></p>
     <p><code>  execerror(s, "argument out of domain");</code></p>
     <p><code> } else if (errno == ERANGE) {</code></p>
     <p><code>  errno = 0;</code></p>
     <p><code>  execerror(s, "result out of range");</code></p>
     <p><code> }</code></p>
     <p><code> return d;</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.7.19 <code>mbox</code></p>
     </title>
     <p><code>From: Polyhedron Software Ltd &lt;100013.461@CompuServe.COM&gt;</code></p>
     <p><code>To: "&gt;INTERNET:bwk@research.att.com" &lt;bwk@research.att.com &gt;</code> </p>
     <p><code>Subject: Message from Internet</code></p>
     <p><code>Date: 10 May 91 04:07:07 EDT</code></p>
     <p><code>Message-Id: &lt;"910510080707 100013.461 CHE27-1"@CompuServe.COM&gt;</code></p>
     <empty-line/>
     <p><code>Got your message. I'll pass it on to Tony. We haven't noticed any</code></p>
     <p><code>errors at all in CompuServe mail, so far.</code></p>
     <empty-line/>
     <p><code>Regards</code></p>
     <empty-line/>
     <p><code>Graham Wood</code></p>
     <empty-line/>
     <p><code>From kam Thu May 9 10:58:06 EDT 1991</code></p>
     <p><code>tony fritzpatrick called from england. he had spoken to you</code></p>
     <p><code>last week about compuserve.</code></p>
     <p><code>the number is:</code></p>
     <p><code>100013,461</code></p>
     <empty-line/>
     <p><code>this is regarding the HOC6 listing.</code></p>
     <empty-line/>
     <p><code>he will call you back tomorrow</code></p>
     <empty-line/>
     <p><code>From pipe!subll276 Fri May 3 10:38:29 EDT 1991</code></p>
     <p><code>Message to: BK</code></p>
     <empty-line/>
     <p><code>From: Tony Fitzpatrick</code></p>
     <p><code>ECL</code></p>
     <p><code>Highlands Farm</code></p>
     <p><code>Greys Road</code></p>
     <p><code>Henley OXON, RG 94 PS</code></p>
     <p><code>ENGLAND</code></p>
     <empty-line/>
     <p><code>Telephone: 0491 - 575-989 (country code 45)</code></p>
     <empty-line/>
     <p><code>FAX: 0491 576 557</code></p>
     <empty-line/>
     <p><code>1. H would like permission</code></p>
     <p><code>   (which has already been granted by publisher) to</code></p>
     <p><code>   use HUC 6 program — commercial software.</code></p>
     <empty-line/>
     <p><code>2. Is the listing available on floppy disk?</code></p>
     <empty-line/>
     <p><code>3. Thank you for a very interesting and useful book.</code></p>
     <empty-line/>
     <p><code>4. He left his fax # and telephone #. He wasn't sure of the country code.</code></p>
     <p><code>   He would appreciate hearing from you via fax.</code></p>
     <empty-line/>
     <p><code>sub 11276</code></p>
    </section>
    <section>
     <title>
      <p>3.7.20 <code>symbol.c</code></p>
     </title>
     <p><code>#include "hoc.h"</code></p>
     <p><code>#include "y.tab.h"</code></p>
     <empty-line/>
     <p><code>static Symbol *symlist =0; /* symbol table: linked list */</code></p>
     <empty-line/>
     <p><code>Symbol *lookup(s) /* find s in symbol table */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> Symbol *sp;</code></p>
     <empty-line/>
     <p><code> for (sp = symlist; sp != (Symbol*)0; sp = sp-&gt;next)</code></p>
     <p><code>  if (strcmp(sp-&gt;name, s) == 0)</code></p>
     <p><code>   return sp;</code></p>
     <p><code> return 0; /* 0 ==&gt; not found */</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>Symbol *install(s, t, d) /* install s in symbol table */</code></p>
     <p><code> char *s;</code></p>
     <p><code> int t;</code></p>
     <p><code> double d;</code></p>
     <p><code>{</code></p>
     <p><code> Symbol *sp;</code></p>
     <p><code> char *emalloc();</code></p>
     <empty-line/>
     <p><code> sp = (Symbol*)emalloc(sizeof(Symbol));</code></p>
     <p><code> sp-&gt;name = emalloc(strlen(s)+1); /* +1 for '\0' */</code></p>
     <p><code> strcpy(sp-&gt;name, s);</code></p>
     <p><code> sp-&gt;type = t;</code></p>
     <p><code> sp-&gt;u.val = d;</code></p>
     <p><code> sp-&gt;next = symlist; /* put at front of list */</code></p>
     <p><code> symlist = sp;</code></p>
     <p><code> return sp;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>char *emalloc(n) /* check return from malloc */</code></p>
     <p><code> unsigned n;</code></p>
     <p><code>{</code></p>
     <p><code> char *p, *malloc();</code></p>
     <empty-line/>
     <p><code> p = malloc(n);</code></p>
     <p><code> if (p == 0)</code></p>
     <p><code>  execerror("out of memory", (char*)0);</code></p>
     <p><code> return p;</code></p>
     <p><code>}</code></p>
    </section>
   </section>
   <section>
    <title>
     <p>3.8 Всякая всячина</p>
    </title>
    <section>
     <title>
      <p>3.8.1 <code>addup1</code></p>
     </title>
     <p><code>awk '{ s += $'$1' }</code></p>
     <p><code> END { print s }'</code></p>
    </section>
    <section>
     <title>
      <p>3.8.2. <code>addup2</code></p>
     </title>
     <p><code>awk '</code></p>
     <p><code> BEGIN { n = '$1' }</code></p>
     <p><code> { for (i = 1; i &lt;= n; i++)</code></p>
     <p><code>  sum[i] += $i</code></p>
     <p><code> }</code></p>
     <p><code> END { for (i = 1; i &lt;= n; i++) {</code></p>
     <p><code>  printf "%6g ", sum[i]</code></p>
     <p><code>  total += sum[i]</code></p>
     <p><code>  }</code></p>
     <p><code> printf "; total = %6g\n", total</code></p>
     <p><code>}'</code></p>
    </section>
    <section>
     <title>
      <p>3.8.3 <code>backup</code></p>
     </title>
     <p><code>push -v panther $* /usr/bwk/eff/Code</code></p>
    </section>
    <section>
     <title>
      <p>3.8.4 <code>backwards</code></p>
     </title>
     <p><code># backwards: print input in backward line order</code></p>
     <p><code>awk ' { line[NR] = $0 }</code></p>
     <p><code>END { for (i = NR; i &gt; 0; i--) print line[i] } ' $*</code></p>
    </section>
    <section>
     <title>
      <p>3.8.5 <code>badpick.c</code></p>
     </title>
     <p><code>pick(s) /* offer choice of s */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> fprintf("%s? ", s);</code></p>
     <p><code> if (ttyin() == 'y')</code></p>
     <p><code>  printf("%s\n", s);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.6 <code>bundle</code></p>
     </title>
     <p><code># bundle: group files into distribution package</code></p>
     <empty-line/>
     <p><code>echo '# To unbundle, sh this file'</code></p>
     <p><code>for i</code></p>
     <p><code>do</code></p>
     <p><code> echo "echo $i 1&gt;&amp;2"</code></p>
     <p><code> echo "cat &gt;$i &lt;&lt;'End of $i'"</code></p>
     <p><code> cat $i</code></p>
     <p><code> echo "End of $i"</code></p>
     <p><code>done</code></p>
    </section>
    <section>
     <title>
      <p>3.8.7 <code>cal</code></p>
     </title>
     <p><code># cal: nicer interface to /usr/bin/cal</code></p>
     <empty-line/>
     <p><code>case $# in</code></p>
     <p><code>0) set `date`; m=$2; y=$6 ;; # no args: use today</code></p>
     <p><code>1) m=$1; set `date`; y=$6 ;; #1 arg: use this year</code></p>
     <p><code>*) m=$1; y=$2 ;; #2 args: month and year</code></p>
     <p><code>esac</code></p>
     <empty-line/>
     <p><code>case $m in</code></p>
     <p><code>jan*|Jan*) m=1 ;;</code></p>
     <p><code>feb*|Feb*) m=2 ;;</code></p>
     <p><code>mar*|Mar*) m=3 ;;</code></p>
     <p><code>apr*|Apr*) m=4 ;;</code></p>
     <p><code>may*|May*) m=5 ;;</code></p>
     <p><code>jun*|Jun*) m=6 ;;</code></p>
     <p><code>jul*|Jul*) m=7 ;;</code></p>
     <p><code>aug*|Aug*) m=8 ;;</code></p>
     <p><code>sep*|Sep*) m=9 ;;</code></p>
     <p><code>oct*|Oct*) m=10 ;;</code></p>
     <p><code>nov*|Nov*) m=11 ;;</code></p>
     <p><code>dec*|Dec*) m=12 ;;</code></p>
     <p><code>[1-9]|10|11|12) ;; # numeric month</code></p>
     <p><code>*) y=$m; m="" ;; # plain year</code></p>
     <p><code>esac</code></p>
     <empty-line/>
     <p><code>/usr/bin/cal $m $y # run the real one</code></p>
    </section>
    <section>
     <title>
      <p>3.8.8 <code>calendar1</code></p>
     </title>
     <p><code># calendar: version 1 -- today only</code></p>
     <p><code>awk &lt;$HOME/calendar '</code></p>
     <p><code> BEGIN { split("'"`date`"'", date) }</code></p>
     <p><code> $1 == date[2] &amp;&amp; $2 == date[3]</code></p>
     <p><code>' | mail $NAME</code></p>
    </section>
    <section>
     <title>
      <p>3.8.9 <code>calendar2</code></p>
     </title>
     <p><code># calendar: version 2 -- today only, no quotes</code></p>
     <p><code>(date; cat $HOME/calendar) |</code></p>
     <p><code>awk '</code></p>
     <p><code> NR == 1 { mon = $2; day = $3 } # set the date</code></p>
     <p><code> NR &gt; 1 &amp;&amp; $1 == mon &amp;&amp; $2 == day # print calendar lines</code></p>
     <p><code>' | mail $NAME</code></p>
    </section>
    <section>
     <title>
      <p>3.8.10 <code>calendar3</code></p>
     </title>
     <p><code># calendar: version 3 -- today and tomorrow</code></p>
     <p><code>awk &lt;$HOME/calendar '</code></p>
     <p><code> BEGIN {</code></p>
     <p><code>  x = "Jan 31 Feb 28 Mar 31 Apr 30 May 31 Jun 30 " \</code></p>
     <p><code>      "Jul 31 Aug 31 Sep 30 Oct 31 Nov 30 Dec 31 Jan 31"</code></p>
     <p><code>  split(x, data)</code></p>
     <p><code>  for (i = 1; i &lt; 24; i += 2) {</code></p>
     <p><code>   days[data[i]] = data[i+1]</code></p>
     <p><code>   nextmon[data[i]] = data[i+2]</code></p>
     <p><code>  }</code></p>
     <p><code>  split("'"`date`"'", date)</code></p>
     <p><code>  mon1 = date[2]; day1 = date[3]</code></p>
     <p><code>  mon2 = mon1; day2 = day1 + 1</code></p>
     <p><code>  if (day1 &gt;= days[mon1]) {</code></p>
     <p><code>   day2 = 1</code></p>
     <p><code>   mon2 = nextmon[mon1]</code></p>
     <p><code>  }</code></p>
     <p><code> }</code></p>
     <p><code> $1 == mon1 &amp;&amp; $2 == day1 || $1 == mon2 &amp;&amp; $2 == day2</code></p>
     <p><code>' | mail $NAME</code></p>
    </section>
    <section>
     <title>
      <p>3.8.11 <code>cat0.c</code></p>
     </title>
     <p><code>/* cat: minimal version */</code></p>
     <p><code>#define SIZE 512 /* arbitrary */</code></p>
     <empty-line/>
     <p><code>main() {</code></p>
     <p><code> char buf[SIZE];</code></p>
     <p><code> int n;</code></p>
     <empty-line/>
     <p><code> while ((n = read(0, buf, sizeof buf)) &gt; 0)</code></p>
     <p><code>  write(1, buf, n);</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.12 <code>checkmail.c</code></p>
     </title>
     <p><code>/* checkmail: watch user's mailbox */</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;sys/types.h&gt;</code></p>
     <p><code>#include &lt;sys/stat.h&gt;</code></p>
     <empty-line/>
     <p><code>char *progname;</code></p>
     <p><code>char *maildir = "/usr/spool/mail"; /* system dependent */</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> struct stat buf;</code></p>
     <p><code> char *name, *getlogin();</code></p>
     <p><code> int lastsize = 0;</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> if ((name = getlogin()) == NULL)</code></p>
     <p><code>  error("can't get login name", (char*)0);</code></p>
     <p><code> if (chdir(maildir) == -1)</code></p>
     <p><code>  error("can't cd to %s", maildir);</code></p>
     <p><code> for (;;) {</code></p>
     <p><code>  if (stat(name, &amp;buf) == -1) /* no mailbox */</code></p>
     <p><code>   buf.st_size = 0;</code></p>
     <p><code>  if (buf.st_size &gt; lastsize)</code></p>
     <p><code>   fprintf(stderr, "\nYou have mail\007\n");</code></p>
     <p><code>  lastsize = buf.st_size;</code></p>
     <p><code>  sleep(60);</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
     <p><code>#include "error.c"</code></p>
    </section>
    <section>
     <title>
      <p>3.8.13 <code>checkmail.sh</code></p>
     </title>
     <p><code># checkmail: watch mailbox for growth</code></p>
     <empty-line/>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <p><code>MAIL=/usr/spool/mail/`getname` # system dependent</code></p>
     <empty-line/>
     <p><code>t=${1-60}</code></p>
     <empty-line/>
     <p><code>x="`ls -l $MAIL`"</code></p>
     <p><code>while :</code></p>
     <p><code>do</code></p>
     <p><code> y="`ls -l $MAIL`"</code></p>
     <p><code> echo $x $y x="$y"</code></p>
     <p><code> sleep $t</code></p>
     <p><code>done | awk '$4 &lt; $12 { print "You have mail" }'</code></p>
    </section>
    <section>
     <title>
      <p>3.8.14 <code>cp.c</code></p>
     </title>
     <p><code>/* cp: minimal version */</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#define PERMS 0644 /* RW for owner, R for group, others */</code></p>
     <empty-line/>
     <p><code>char *progname;</code></p>
     <empty-line/>
     <p><code>main(argc, argv) /* cp: copy f1 to f2 */</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> char buf[BUFSIZ];</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> if (argc != 3)</code></p>
     <p><code>  error("Usage: %s from to", progname);</code></p>
     <p><code> if ((f1 = open(argv[1], 0)) == -1)</code></p>
     <p><code>  error("can't open %s", argv[1]);</code></p>
     <p><code> if ((f2 = creat(argv[2] , PERMS)) == -1)</code></p>
     <p><code>  error("can't create %s", argv[2]);</code></p>
     <p><code> while ((n = read(f1, buf, BUFSIZ)) &gt; 0)</code></p>
     <p><code>  if (write(f2, buf, n) != n)</code></p>
     <p><code>   error("write error", (char*)0);</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
     <p><code>#include "error.c"</code></p>
    </section>
    <section>
     <title>
      <p>3.8.15 <code>doctype</code></p>
     </title>
     <p><code># doctype: synthesize proper command line for troff</code></p>
     <p><code>echo -n "cat $* | "</code></p>
     <p><code>egrep -h '^\.(EQ|TS|\[|PS|IS|PP)' $* |</code></p>
     <p><code>sort -u | awk '</code></p>
     <p><code>/^\.PP/ { ms++ }</code></p>
     <p><code>/^\.EQ/ { eqn++ }</code></p>
     <p><code>/^\.TS/ { tbl++ }</code></p>
     <p><code>/^\.PS/ { pic++ }</code></p>
     <p><code>/^\.IS/ { ideal++ }</code></p>
     <p><code>/^\.\[/ { refer++ }</code></p>
     <p><code>END {</code></p>
     <p><code> if (refer &gt; 0) printf "refer | "</code></p>
     <p><code> if (pic &gt; 0) printf "pic | "</code></p>
     <p><code> if (ideal &gt; 0) printf "ideal | "</code></p>
     <p><code> if (tbl &gt; 0) printf "tbl | "</code></p>
     <p><code> if (eqn &gt; 0) printf "eqn | "</code></p>
     <p><code> printf "troff "</code></p>
     <p><code> if (ms &gt; 0) printf "-ms"</code></p>
     <p><code> printf "\n"</code></p>
     <p><code>}'</code></p>
    </section>
    <section>
     <title>
      <p>3.8.16 <code>double</code></p>
     </title>
     <p><code>awk '</code></p>
     <p><code>FILENAME != prevfile { # new file</code></p>
     <p><code> NR = 1                # reset line number</code></p>
     <p><code> prevfile = FILENAME</code></p>
     <p><code>}</code></p>
     <p><code>NF &gt; 0 {</code></p>
     <p><code> if ($1 == lastword)</code></p>
     <p><code>  printf "double %s, file %s, line %d\n" ,$1,FILENAME,NR</code></p>
     <p><code> for (i = 2; i &lt;= NF; i++)</code></p>
     <p><code>  if ($i == $(i-1))</code></p>
     <p><code>   printf "double %s, file %s, line %d\n" ,$i, FILENAME ,NR</code></p>
     <p><code> if (NF &gt; 0)</code></p>
     <p><code>  lastword = $NF</code></p>
     <p><code>}' $*</code></p>
    </section>
    <section>
     <title>
      <p>3.8.17 <code>efopen.c</code></p>
     </title>
     <p><code>FILE *efopen(file, mode) /* fopen file, die if can't */</code></p>
     <p><code> char *file, *mode;</code></p>
     <p><code>{</code></p>
     <p><code> FILE *fp, *fopen();</code></p>
     <p><code> extern char *progname;</code></p>
     <empty-line/>
     <p><code> if ((fp = fopen(file, mode)) != NULL)</code></p>
     <p><code>  return fp;</code></p>
     <p><code> fprintf (stderr, "%s: can't open file %s mode %s\n",</code></p>
     <p><code>  progname, file, mode);</code></p>
     <p><code> exit(1);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.18 <code>error.c</code></p>
     </title>
     <p><code>error(s1, s2) /* print error message and die */</code></p>
     <p><code> char *s1, *s2;</code></p>
     <p><code>{</code></p>
     <p><code> extern int errno, sys_nerr;</code></p>
     <p><code> extern char *sys_errlist[], *progname;</code></p>
     <empty-line/>
     <p><code> if (progname)</code></p>
     <p><code>  fprintf(stderr, "%s: ", progname);</code></p>
     <p><code> fprintf(stderr, s1, s2);</code></p>
     <p><code> if (errno &gt; 0 &amp;&amp; errno &lt; sys_nerr)</code></p>
     <p><code>  fprintf (stderr, " (%s)", sys_errlist[errno]);</code></p>
     <p><code> fprintf(stderr, "\n");</code></p>
     <p><code> exit(1);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.19 <code>field1</code></p>
     </title>
     <p><code>awk '{ print $'$1' }'</code></p>
    </section>
    <section>
     <title>
      <p>3.8.20 <code>field2</code></p>
     </title>
     <p><code>awk "{ print \$$1 }"</code></p>
    </section>
    <section>
     <title>
      <p>3.8.21 <code>fold</code></p>
     </title>
     <p><code># fold: fold long lines</code></p>
     <p><code>sed 's/\(-&gt;/ /g' $* | # convert tabs to spaces</code></p>
     <p><code>awk '</code></p>
     <p><code> BEGIN {</code></p>
     <p><code>  N = 80                   # folds at column 80</code></p>
     <p><code>  for (i = 1; i &lt;= N; i++) # make a string of blanks</code></p>
     <p><code>   blanks = blanks " "</code></p>
     <p><code> }</code></p>
     <p><code> { if ((n = length($0)) &lt;= N)</code></p>
     <p><code>  print</code></p>
     <p><code>  else {</code></p>
     <p><code>   for (i = 1; n &gt; N; n -= N) {</code></p>
     <p><code>    printf "%s\\\n", substr($0, i ,N)</code></p>
     <p><code>    i += N;</code></p>
     <p><code>   }</code></p>
     <p><code>   printf "%s%s\n", substr(blanks, 1, N-n), substr($0, i)</code></p>
     <p><code>  }</code></p>
     <p><code> } '</code></p>
    </section>
    <section>
     <title>
      <p>3.8.22 <code>frequent</code></p>
     </title>
     <p><code>cat $* |</code></p>
     <p><code>tr -sc A-Za-z '\012' |</code></p>
     <p><code>sort |</code></p>
     <p><code>uniq -с |</code></p>
     <p><code>sort -n |</code></p>
     <p><code>tail |</code></p>
     <p><code>5</code></p>
    </section>
    <section>
     <title>
      <p>3.8.23 <code>frequent2</code></p>
     </title>
     <p><code>sed 's/[ \(-&gt;][ \(-&gt;]*/\</code></p>
     <p><code>/g' $* | sort | uniq -с | sort -nr | sed 10q</code></p>
    </section>
    <section>
     <title>
      <p>3.8.24 <code>get</code></p>
     </title>
     <p><code># get: extract file from history</code></p>
     <empty-line/>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <empty-line/>
     <p><code>VERSION=0</code></p>
     <p><code>while test "$1" != ""</code></p>
     <p><code>do</code></p>
     <p><code> case "$1" in</code></p>
     <p><code> -i) INPUT=$2; shift ;;</code></p>
     <p><code> -o) OUTPUT=$2; shift ;;</code></p>
     <p><code> -[0-9]) VERSION=$1 ;;</code></p>
     <p><code> -*) echo "get: Unknown argument $i" 1&gt;&amp;2; exit 1 ;;</code></p>
     <p><code> *) case "$OUTPUT" in</code></p>
     <p><code>  "") OUTPUT=$1 ;;</code></p>
     <p><code>  *) INPUT=$1.H ;;</code></p>
     <p><code>  esac</code></p>
     <p><code> esac</code></p>
     <p><code> shift</code></p>
     <p><code>done</code></p>
     <p><code>OUTPUT=${OUTPUT?"Usage: get [-o outfile] [-i file.H] file"}</code></p>
     <p><code>INPUT=${INPUT-$OUTPUT.H}</code></p>
     <p><code>test -r $INPUT || { echo "get: no file $INPUT" 1&gt;&amp;2; exit 1; }</code></p>
     <p><code>trap 'rm -f /tmp/get.[ab]$$; exit 1' 1 2 15</code></p>
     <p><code># split into current version and editing commands</code></p>
     <p><code>sed &lt;$INPUT -n '1,/^@@@/w /tmp/get.a'$$'</code></p>
     <p><code>/^@@@/,$w /tmp/get.b'$$</code></p>
     <empty-line/>
     <p><code># perform the edits</code></p>
     <p><code>awk &lt;/tmp/get.b$$ '</code></p>
     <p><code> /^@@@/ { count++ }</code></p>
     <p><code> !/^@@@/ &amp;&amp; count &gt; 0 &amp;&amp; count &lt;= - '$VERSION'</code></p>
     <p><code> END { print "$d"; print "w", "'$OUTPUT'" }</code></p>
     <p><code>' | ed - /tmp/get.a$$</code></p>
     <p><code>rm -f /tmp/get.[ab]$$</code></p>
    </section>
    <section>
     <title>
      <p>3.8.25 <code>get.с</code></p>
     </title>
     <p><code>get(fd, pos, buf, n) /* read n bytes from position pos */</code></p>
     <p><code> int fd, n;</code></p>
     <p><code> long pos;</code></p>
     <p><code> char *buf;</code></p>
     <p><code>{</code></p>
     <p><code> if (lseek(fd, pos, 0) == -1) /* get to pos */</code></p>
     <p><code>  return -1;</code></p>
     <p><code> return read(fd, buf, n);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.26 <code>getname</code></p>
     </title>
     <p><code>who am i | sed 's/ .*//'</code></p>
    </section>
    <section>
     <title>
      <p>3.8.27 <code>idiff.c</code></p>
     </title>
     <p><code>/* idiff: interactive diff */</code></p>
     <empty-line/>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;ctype.h&gt;</code></p>
     <empty-line/>
     <p><code>char *progname;</code></p>
     <p><code>#define HUGE 10000 /* large number of lines */</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> FILE *fin, *fout, *f1, *f2, *efopen();</code></p>
     <p><code> char buf[BUFSIZ], *mktemp();</code></p>
     <p><code> char *diffout = "idiff.XXXXXX";</code></p>
     <p><code> progname = argv[0];</code></p>
     <empty-line/>
     <p><code> if (argc != 3) {</code></p>
     <p><code>  fprintf(stderr, "Usage: idiff file1 file2\n");</code></p>
     <p><code>  exit(1);</code></p>
     <p><code> }</code></p>
     <p><code> f1 = efopen(argv[1], "r");</code></p>
     <p><code> f2 = efopen(argv[2], "r");</code></p>
     <p><code> fout = efopen("idiff.out", "w");</code></p>
     <p><code> mktemp(diffout);</code></p>
     <p><code> sprintf(buf, "diff %s %s &gt;%s", argv[1], argv[2], diffout);</code></p>
     <p><code> system(buf);</code></p>
     <p><code> fin = efopen(diffout, "r");</code></p>
     <p><code> idiff(f1, f2, fin, fout); unlink(diffout);</code></p>
     <p><code> printf("%s output in file idiff.out\n", progname);</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>idiff(f1, f2, fin, fout) /* process diffs */</code></p>
     <p><code> FILE *f1, *f2, *fin, *fout;</code></p>
     <p><code>{</code></p>
     <p><code> char *tempfile = "idiff.XXXXXX";</code></p>
     <p><code> char buf[BUFSIZ], buf2[BUFSIZ], *mktemp();</code></p>
     <p><code> FILE *ft, *efopen();</code></p>
     <p><code> int cmd, n, from1, to1, from2, to2, nf1, nf2;</code></p>
     <empty-line/>
     <p><code> mktemp(tempfile);</code></p>
     <p><code> nf1 = nf2 = 0;</code></p>
     <p><code> while (fgets(buf, sizeof buf, fin) != NULL) {</code></p>
     <p><code>  parse(buf, &amp;from1, &amp;to1, &amp;cmd, &amp;from2, &amp;to2);</code></p>
     <p><code>  n = to1-from1 + to2-from2 + 1; /* #lines from diff */</code></p>
     <p><code>  if (cmd == 'c')</code></p>
     <p><code>   n += 2;</code></p>
     <p><code>  else if (cmd == 'a')</code></p>
     <p><code>   from1++;</code></p>
     <p><code>  else if (cmd == 'd')</code></p>
     <p><code>   from2++;</code></p>
     <p><code>  printf("%s", buf);</code></p>
     <p><code>  while (n-- &gt; 0) {</code></p>
     <p><code>   fgets(buf, sizeof buf, fin);</code></p>
     <p><code>   printf("%s", buf);</code></p>
     <p><code>  }</code></p>
     <p><code>  do {</code></p>
     <p><code>   printf("? ");</code></p>
     <p><code>   fflush(stdout);</code></p>
     <p><code>   fgets(buf, sizeof buf, stdin);</code></p>
     <p><code>   switch (buf[0]) {</code></p>
     <p><code>   case '&gt;':</code></p>
     <p><code>    nskip(f1, to1-nf1);</code></p>
     <p><code>    ncopy(f2, to2-nf2, fout);</code></p>
     <p><code>    break;</code></p>
     <p><code>   case '&lt;':</code></p>
     <p><code>    nskip(f2, to2-nf2);</code></p>
     <p><code>    ncopy(f1, to1-nf1, fout);</code></p>
     <p><code>    break;</code></p>
     <p><code>   case 'e':</code></p>
     <p><code>    ncopy(f1, from1-1-nf1, fout);</code></p>
     <p><code>    nskip(f2, from2-1-nf2);</code></p>
     <p><code>    ft = efopen(tempfile, "w");</code></p>
     <p><code>    ncopy(f1, to1+1-from1, ft);</code></p>
     <p><code>    fprintf(ft, "--- \n");</code></p>
     <p><code>    ncopy(f2, to2+1-from2, ft);</code></p>
     <p><code>    fclose(ft);</code></p>
     <p><code>    sprintf(buf2, "ed %s", tempfile);</code></p>
     <p><code>    system(buf2);</code></p>
     <p><code>    ft = efopen(tempfile, "r");</code></p>
     <p><code>    ncopy(ft, HUGE, fout);</code></p>
     <p><code>    fclose(ft);</code></p>
     <p><code>    break;</code></p>
     <p><code>   case '!':</code></p>
     <p><code>    system(buf+1);</code></p>
     <p><code>    printf("!\n");</code></p>
     <p><code>    break;</code></p>
     <p><code>   default:</code></p>
     <p><code>    printf("&lt; or &gt; or e or !\n");</code></p>
     <p><code>    break;</code></p>
     <p><code>   }</code></p>
     <p><code>  } while (buf[0]!= '&lt;' &amp;&amp; buf[0]!= '&gt;' &amp;&amp; buf[0]! = 'e');</code></p>
     <p><code>  nf1 = to1;</code></p>
     <p><code>  nf2 = to2;</code></p>
     <p><code> }</code></p>
     <p><code> ncopy(f1, HUGE, fout); /* can fail on very long files */</code></p>
     <p><code> unlink(tempfile);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>parse(s, pfrom1, pto1, pcmd, pfrom2, pto2)</code></p>
     <p><code> char *s;</code></p>
     <p><code> int *pcmd, *pfrom1, *pto1, *pfrom2, *pto2;</code></p>
     <p><code>{</code></p>
     <p><code>#define a2i(p) while (isdigit(*s)) \</code></p>
     <p><code> p = 10*(p) + *s++ - '0'</code></p>
     <empty-line/>
     <p><code> *pfrom1 = *pto1 = *pfrom2 = *pto2 = 0;</code></p>
     <p><code> a2i(*pfrom1);</code></p>
     <p><code> if (*s == ',') {</code></p>
     <p><code>  s++;</code></p>
     <p><code>  a2i(*pto1);</code></p>
     <p><code> } else</code></p>
     <p><code>  *pto1 = *pfrom1;</code></p>
     <p><code> *pcmd = *s++;</code></p>
     <p><code> a2i(*pfrom2);</code></p>
     <p><code> if (*s == ',') {</code></p>
     <p><code>  s++;</code></p>
     <p><code>  a2i(*pto2);</code></p>
     <p><code> } else</code></p>
     <p><code>  *pto2 = *pfrom2;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>nskip(fin, n) /* skip n lines of file fin */</code></p>
     <p><code> FILE *fin;</code></p>
     <p><code>{</code></p>
     <p><code> char buf[BUFSIZ];</code></p>
     <empty-line/>
     <p><code> while (n-- &gt; 0)</code></p>
     <p><code>  fgets(buf, sizeof buf, fin);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>ncopy(fin, n, fout) /* copy n lines from fin to fout */</code></p>
     <p><code> FILE *fin, *fout;</code></p>
     <p><code>{</code></p>
     <p><code> char buf[BUFSIZ];</code></p>
     <p><code> while (n-- &gt; 0) {</code></p>
     <p><code>  if (fgets(buf, sizeof buf, fin) == NULL)</code></p>
     <p><code>   return;</code></p>
     <p><code>  fputs(buf, fout);</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>#include "efopen.c"</code></p>
    </section>
    <section>
     <title>
      <p>3.8.28 <code>makefile</code></p>
     </title>
     <p><code>files: files.o files1.o directory.o</code></p>
     <p><code>      cc files.o files1.o directory.o -o files</code></p>
     <empty-line/>
     <p><code>p0: p0.c ttyin0.c</code></p>
     <p><code>      cc p0.c ttyin0.c</code></p>
     <empty-line/>
     <p><code>clean:</code></p>
     <p><code>      rm -f *.o a.out</code></p>
    </section>
    <section>
     <title>
      <p>3.8.29 <code>newer</code></p>
     </title>
     <p><code># newer f: list files newer than f</code></p>
     <p><code>ls -t | sed '/^'$1'$/q'</code></p>
    </section>
    <section>
     <title>
      <p>3.8.30 <code>news1</code></p>
     </title>
     <p><code># news: print news files, version 1</code></p>
     <empty-line/>
     <p><code>HOME=. # debugging only</code></p>
     <p><code>cd . # place holder for /usr/news</code></p>
     <p><code>for i in `ls -t * $HOME/.news_time`</code></p>
     <p><code>do</code></p>
     <p><code> case $i in</code></p>
     <p><code> */.news_time) break ;;</code></p>
     <p><code> *) echo news: $i</code></p>
     <p><code> esac</code></p>
     <p><code>done</code></p>
     <p><code>touch $HOME/.news_time</code></p>
    </section>
    <section>
     <title>
      <p>3.8.31 <code>news2</code></p>
     </title>
     <p><code># news: print news files, version 2</code></p>
     <empty-line/>
     <p><code>HOME=. # debugging only</code></p>
     <p><code>cd . # place holder for /usr/news</code></p>
     <p><code>IFS='</code></p>
     <p><code>' # just a newline</code></p>
     <p><code>for i in `ls -t * $HOME/.news_time 2&gt;&amp;1`</code></p>
     <p><code>do</code></p>
     <p><code>case $i in</code></p>
     <p><code>*' not found') ;;</code></p>
     <p><code>*/.news_time) break ;;</code></p>
     <p><code>*) echo news: $i ;;</code></p>
     <p><code>esac</code></p>
     <p><code>done</code></p>
     <p><code>touch $HOME/.news_time</code></p>
    </section>
    <section>
     <title>
      <p>3.8.32 <code>news3</code></p>
     </title>
     <p><code># news: print news files, final version</code></p>
     <empty-line/>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <p><code>IFS='</code></p>
     <p><code>' # just a newline</code></p>
     <p><code>cd /usr/news</code></p>
     <empty-line/>
     <p><code>for i in `ls -t * $HOME/.news_time 2&gt;&amp;1`</code></p>
     <p><code>do</code></p>
     <p><code> IFS=' '</code></p>
     <p><code> case $i in</code></p>
     <p><code> *' not found') ;;</code></p>
     <p><code> */.news_time) break ;;</code></p>
     <p><code> *) set X`ls -l $i`</code></p>
     <p><code>  echo "</code></p>
     <p><code>$i: ($3) $5 $6 $7</code></p>
     <p><code>"</code></p>
     <p><code>  cat $i</code></p>
     <p><code> esac</code></p>
     <p><code>done</code></p>
     <p><code>touch $HOME/.news_time</code></p>
    </section>
    <section>
     <title>
      <p>3.8.33 <code>nohup</code></p>
     </title>
     <p><code>trap "" 1 15</code></p>
     <p><code>if test -t 2&gt;&amp;1</code></p>
     <p><code>then</code></p>
     <p><code> echo "Sending output to 'nohup.out'"</code></p>
     <p><code> exec nice -5 $* &gt;&gt;nohup.out 2&gt;&amp;1</code></p>
     <p><code>else</code></p>
     <p><code> exec nice -5 $* 2&gt;&amp;1</code></p>
     <p><code>fi</code></p>
    </section>
    <section>
     <title>
      <p>3.8.34 <code>older</code></p>
     </title>
     <p><code># older f: list files older than f</code></p>
     <p><code>ls -tr | sed '/^'$!'$/q'</code></p>
    </section>
    <section>
     <title>
      <p>3.8.35 <code>overwrite1</code></p>
     </title>
     <p><code># overwrite: copy standard input to output after EOF</code></p>
     <p><code># version 1. BUG here</code></p>
     <empty-line/>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <empty-line/>
     <p><code>case $# in</code></p>
     <p><code>1) ;;</code></p>
     <p><code>*) echo 'Usage: overwrite file' 1&gt;&amp;2; exit 2</code></p>
     <p><code>esac</code></p>
     <empty-line/>
     <p><code>new=/tmp/overwr.$$</code></p>
     <p><code>trap 'rm -f $new; exit 1' 1 2 15</code></p>
     <empty-line/>
     <p><code>cat &gt;$new # collect the input</code></p>
     <p><code>cp $new $1 # overwrite the input file</code></p>
     <p><code>rm -f $new</code></p>
    </section>
    <section>
     <title>
      <p>3.8.36 <code>overwrite2</code></p>
     </title>
     <p><code># overwrite: copy standard input to output after EOF</code></p>
     <p><code># version 2. BUG here too</code></p>
     <empty-line/>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <empty-line/>
     <p><code>case $# in</code></p>
     <p><code>1) ;;</code></p>
     <p><code>*) echo 'Usage: overwrite file' 1&gt;&amp;2; exit 2</code></p>
     <p><code>esac</code></p>
     <empty-line/>
     <p><code>new=/tmp/overwr1.$$</code></p>
     <p><code>old=/tmp/overwr2.$$</code></p>
     <p><code>trap 'rm -f $new $old; exit 1' 1 2 15</code></p>
     <empty-line/>
     <p><code>cat &gt;$new # collect the input</code></p>
     <p><code>cp $1 $old # save original file</code></p>
     <empty-line/>
     <p><code>trap '' 1 2 15 # we are committed; ignore signals</code></p>
     <p><code>cp $new $1 # overwrite the input file</code></p>
     <empty-line/>
     <p><code>rm -f $new $old</code></p>
    </section>
    <section>
     <title>
      <p>3.8.37 <code>overwrite3</code></p>
     </title>
     <p><code># overwrite: copy standard input to output after EOF</code></p>
     <p><code># final version</code></p>
     <empty-line/>
     <p><code>opath=$PATH</code></p>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <empty-line/>
     <p><code>case $# in</code></p>
     <p><code>0|1) echo 'Usage: overwrite file cmd [args]' 1&gt;&amp;2; exit 2</code></p>
     <p><code>esac</code></p>
     <empty-line/>
     <p><code>file=$1; shift</code></p>
     <p><code>new=/tmp/overwr1.$$; old=/tmp/overwr2.$$</code></p>
     <p><code>trap 'rm -f $new $old; exit 1' 1 2 15 # clean up files</code></p>
     <empty-line/>
     <p><code>if PATH=$opath &gt;$new # collect input</code></p>
     <p><code>then</code></p>
     <p><code> cp $file $old # save original file</code></p>
     <p><code> trap '' 1 2 15 # we are committed; ignore signals</code></p>
     <p><code> cp $new $file</code></p>
     <p><code>else</code></p>
     <p><code> echo "overwrite: $1 failed, $file unchanged" 1&gt;&amp;2</code></p>
     <p><code> exit 1</code></p>
     <p><code>fi</code></p>
     <p><code>rm -f $new $old</code></p>
    </section>
    <section>
     <title>
      <p>3.8.38 <code>p1.c</code></p>
     </title>
     <p><code>/* p: print input in chunks (version 1) */</code></p>
     <empty-line/>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#define PAGESIZE 22</code></p>
     <p><code>char *progname; /* program name for error message */</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> int i;</code></p>
     <p><code> FILE *fp, *efopen();</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> if (argc == 1)</code></p>
     <p><code>  print(stdin, PAGESIZE);</code></p>
     <p><code> else</code></p>
     <p><code>  for (i = 1; i &lt; argc; i++) {</code></p>
     <p><code>   fp = efopen(argv[i], "r");</code></p>
     <p><code>   print(fp, PAGESIZE);</code></p>
     <p><code>   fclose(fp);</code></p>
     <p><code>  }</code></p>
     <p><code>  exit(0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>print(fp, pagesize) /* print fp in pagesize chunks */</code></p>
     <p><code> FILE *fp;</code></p>
     <p><code> int pagesize;</code></p>
     <p><code>{</code></p>
     <p><code> static int lines = 0; /* number of lines so far */</code></p>
     <p><code> char buf[BUFSIZ];</code></p>
     <empty-line/>
     <p><code> while (fgets(buf, sizeof buf, fp) != NULL)</code></p>
     <p><code>  if (++lines &lt; pagesize)</code></p>
     <p><code>   fputs(buf, stdout);</code></p>
     <p><code>  else {</code></p>
     <p><code>   buf[strlen(buf)-1] = '\0';</code></p>
     <p><code>   fputs(buf, stdout);</code></p>
     <p><code>   fflush(stdout);</code></p>
     <p><code>   ttyin();</code></p>
     <p><code>   lines = 0;</code></p>
     <p><code>  }</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>#include "ttyin1.c"</code></p>
     <p><code>#include "efopen.c"</code></p>
    </section>
    <section>
     <title>
      <p>3.8.39 <code>p2.c</code></p>
     </title>
     <p><code>/* p: print input in chunks (version 2) */</code></p>
     <empty-line/>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#define PAGESIZE 22</code></p>
     <empty-line/>
     <p><code>char *progname; /* program name for error message */</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> FILE *fp, *efopen();</code></p>
     <p><code> int i, pagesize = PAGESIZE;</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> if (argc &gt; 1 &amp;&amp; argv[1][0] == '-') {</code></p>
     <p><code>  pagesize = atoi(&amp;argv[1][1]);</code></p>
     <p><code>  argc--;</code></p>
     <p><code>  argv++;</code></p>
     <p><code> }</code></p>
     <p><code> if (argc == 1)</code></p>
     <p><code>  print(stdin, pagesize);</code></p>
     <p><code> for (i = 1; i &lt; argc; i++) {</code></p>
     <p><code>  fp = efopen(argv[i], "r");</code></p>
     <p><code>  print(fp, pagesize);</code></p>
     <p><code>  fclose(fp);</code></p>
     <p><code> }</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>print(fp, pagesize) /* print fp in pagesize chunks */</code></p>
     <p><code> FILE *fp;</code></p>
     <p><code> int pagesize;</code></p>
     <p><code>{</code></p>
     <p><code> static int lines = 0; /* number of lines so far */</code></p>
     <p><code> char buf[BUFSIZ];</code></p>
     <empty-line/>
     <p><code> while (fgets(buf, sizeof buf, fp) != NULL)</code></p>
     <p><code>  if (++lines &lt; pagesize)</code></p>
     <p><code>   fputs(buf, stdout);</code></p>
     <p><code>  else {</code></p>
     <p><code>   buf[strlen(buf)-1] = '\0';</code></p>
     <p><code>   fputs(buf, stdout);</code></p>
     <p><code>   fflush(stdout);</code></p>
     <p><code>   ttyin();</code></p>
     <p><code>   lines = 0;</code></p>
     <p><code>  }</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>#include "ttyin2.c"</code></p>
     <p><code>#include "efopen.c"</code></p>
    </section>
    <section>
     <title>
      <p>3.8.40 <code>p3.c</code></p>
     </title>
     <p><code>/* p: print input in chunks (version 3) */</code></p>
     <empty-line/>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#define PAGESIZE 22</code></p>
     <p><code>char *progname; /* program name for error message */</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> FILE *fp, *efopen();</code></p>
     <p><code> int i, pagesize = PAGESIZE;</code></p>
     <p><code> char *p, *getenv();</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> if ((p=getenv("PAGESIZE")) != NULL)</code></p>
     <p><code>  pagesize = atoi(p);</code></p>
     <p><code> if (argc &gt; 1 &amp;&amp; argv[1][0] == '-') {</code></p>
     <p><code>  pagesize = atoi(&amp;argv[1][1]);</code></p>
     <p><code>  argc--;</code></p>
     <p><code>  argv++;</code></p>
     <p><code> }</code></p>
     <p><code> if (argc == 1)</code></p>
     <p><code>  print(stdin, pagesize);</code></p>
     <p><code> else</code></p>
     <p><code>  for (i = 1; i &lt; argc; i++) {</code></p>
     <p><code>   fp = efopen(argv[i], "r");</code></p>
     <p><code>   print(fp, pagesize);</code></p>
     <p><code>   fclose(fp);</code></p>
     <p><code>  }</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>print(fp, pagesize) /* print fp in pagesize chunks */</code></p>
     <p><code> FILE *fp;</code></p>
     <p><code> int pagesize;</code></p>
     <p><code>{</code></p>
     <p><code> static int lines = 0; /* number of lines so far */</code></p>
     <p><code> char buf[BUFSIZ];</code></p>
     <empty-line/>
     <p><code> while (fgets(buf, sizeof buf, fp) != NULL)</code></p>
     <p><code>  if (++lines &lt; pagesize)</code></p>
     <p><code>   fputs(buf, stdout);</code></p>
     <p><code>  else {</code></p>
     <p><code>   buf[strlen(buf)-1] = '\0';</code></p>
     <p><code>   fputs(buf, stdout);</code></p>
     <p><code>   fflush(stdout);</code></p>
     <p><code>   ttyin();</code></p>
     <p><code>   lines = 0;</code></p>
     <p><code>  }</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>#include "ttyin2.c"</code></p>
     <p><code>#include "efopen.c"</code></p>
    </section>
    <section>
     <title>
      <p>3.8.41 <code>p4.c</code></p>
     </title>
     <p><code>/* p: print input in chunks (version 4) */</code></p>
     <empty-line/>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#define PAGESIZE 22</code></p>
     <p><code>char *progname; /* program name for error message */</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> FILE *fp, *efopen();</code></p>
     <p><code> int i, pagesize = PAGESIZE;</code></p>
     <p><code> char *p, *getenv(), buf[BUFSIZ];</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> if ((p=getenv("PAGESIZE")) != NULL)</code></p>
     <p><code>  pagesize = atoi(p);</code></p>
     <p><code> if (argc &gt; 1 &amp;&amp; argv[1][0] == '-') {</code></p>
     <p><code>  pagesize = atoi(&amp;argv[1][1]);</code></p>
     <p><code>  argc--;</code></p>
     <p><code>  argv++;</code></p>
     <p><code> }</code></p>
     <p><code> if (argc == 1)</code></p>
     <p><code>  print(stdin, pagesize);</code></p>
     <p><code> else</code></p>
     <p><code>  for (i = 1; i &lt; argc; i++)</code></p>
     <p><code>   switch (spname(argv[i], buf)) {</code></p>
     <p><code>   case -1: /* no match possible */</code></p>
     <p><code>    fp = efopen(argv[i], "r");</code></p>
     <p><code>    break;</code></p>
     <p><code>   case 1: /* corrected */</code></p>
     <p><code>    fprintf (stderr, "\"%s\"? ", buf);</code></p>
     <p><code>    if (ttyin() == 'n')</code></p>
     <p><code>     break;</code></p>
     <p><code>    argv[i] = buf;</code></p>
     <p><code>    /* fall through... */</code></p>
     <p><code>   case 0: /* exact match */</code></p>
     <p><code>    fp = efopen(argv[i], "r");</code></p>
     <p><code>    print(fp, pagesize);</code></p>
     <p><code>    fclose(fp);</code></p>
     <p><code>   }</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>print(fp, pagesize) /* print fp in pagesize chunks */</code></p>
     <p><code> FILE *fp;</code></p>
     <p><code> int pagesize;</code></p>
     <p><code>{</code></p>
     <p><code> static int lines = 0; /* number of lines so far */</code></p>
     <p><code> char buf[BUFSIZ];</code></p>
     <empty-line/>
     <p><code> while (fgets(buf, sizeof buf, fp) != NULL)</code></p>
     <p><code>  if (++lines &lt; pagesize) fputs(buf, stdout);</code></p>
     <p><code>  else {</code></p>
     <p><code>   buf[strlen(buf)-1] = '\0';</code></p>
     <p><code>   fputs(buf, stdout);</code></p>
     <p><code>   fflush(stdout);</code></p>
     <p><code>   ttyin();</code></p>
     <p><code>   lines = 0;</code></p>
     <p><code>  }</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>#include "ttyin2.c"</code></p>
     <p><code>#include "efopen.c"</code></p>
     <p><code>#include "spname.c"</code></p>
    </section>
    <section>
     <title>
      <p>3.8.42 <code>pick1</code></p>
     </title>
     <p><code># pick: select arguments</code></p>
     <empty-line/>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <empty-line/>
     <p><code>for i # for each argument</code></p>
     <p><code>do</code></p>
     <p><code> echo -n "$i? " &gt;/dev/tty</code></p>
     <p><code> read response</code></p>
     <p><code> case $response in</code></p>
     <p><code> y*) echo $i ;;</code></p>
     <p><code> q*) break</code></p>
     <p><code> esac</code></p>
     <p><code>done &lt;/dev/tty</code></p>
    </section>
    <section>
     <title>
      <p>3.8.43 <code>pick.c</code></p>
     </title>
     <p><code>/* pick: offer choice on each argument */</code></p>
     <empty-line/>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>char *progname; /* program name for error message */</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> int i;</code></p>
     <p><code> char buf[BUFSIZ];</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> if (argc == 2 &amp;&amp; strcmp(argv[1], "-") == 0) /* pick - */</code></p>
     <p><code>  while (fgets(buf, sizeof buf, stdin) != NULL) {</code></p>
     <p><code>   buf[strlen(buf)-1] = '\0'; /* drop newline */</code></p>
     <p><code>   pick(buf);</code></p>
     <p><code>  }</code></p>
     <p><code> for (i = 1; i &lt; argc; i++)</code></p>
     <p><code>  pick(argv[i]);</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>pick(s) /* offer choice of s */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> fprintf(stderr, "%s? ", s);</code></p>
     <p><code> if (ttyin() == 'y')</code></p>
     <p><code>  printf("%s\n", s);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>#include "ttyin2.c"</code></p>
     <p><code>#include "efopen.c"</code></p>
    </section>
    <section>
     <title>
      <p>3.8.44 <code>prpages</code></p>
     </title>
     <p><code># prpages: compute number of pages that pr will print</code></p>
     <p><code>wc $* |</code></p>
     <p><code>awk '!/ total$/ { n += int(($1+55) / 56) }</code></p>
     <p><code> END { print n }'</code></p>
    </section>
    <section>
     <title>
      <p>3.8.45 <code>put</code></p>
     </title>
     <p><code># put: install file into history</code></p>
     <empty-line/>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <empty-line/>
     <p><code>case $# in</code></p>
     <p><code>1) HIST=$1.H ;;</code></p>
     <p><code>*) echo 'Usage: put file' 1&gt;&amp;2; exit 1 ;;</code></p>
     <p><code>esac</code></p>
     <p><code>if test ! -r $1</code></p>
     <p><code>then</code></p>
     <p><code> echo "put: can't open $1" 1&gt;&amp;2</code></p>
     <p><code> exit 1</code></p>
     <p><code>fi</code></p>
     <p><code>trap 'rm -f /tmp/put.[ab]$$; exit 1 12 15</code></p>
     <p><code>echo -n 'Summary: '</code></p>
     <p><code>read Summary</code></p>
     <empty-line/>
     <p><code>if get -o /tmp/put.a$$ $1 # previous version</code></p>
     <p><code>then # merge pieces</code></p>
     <p><code> cp $1 /tmp/put.b$$ # current version</code></p>
     <p><code> echo `getname` `date` $Summary" &gt;&gt;/tmp/put.b$$</code></p>
     <p><code> diff -e $1 /tmp/put.a$$ &gt;&gt;/tmp/put.b$$ # latest diffs</code></p>
     <p><code> sed -n '/^@@@/,$р' &lt;$HIST &gt;&gt;/tmp/put.b$$ # old diffs</code></p>
     <p><code> overwrite $HIST cat /tmp/put.b$$ # put it back</code></p>
     <p><code>else # make a new one</code></p>
     <p><code> echo "put: creating $HIST"</code></p>
     <p><code> cp $1 $HIST</code></p>
     <p><code> echo "@@@ `getname` `date` $Summary" &gt;&gt;$HIST</code></p>
     <p><code>fi</code></p>
     <p><code>rm -f /tmp/put.[ab]$$</code></p>
    </section>
    <section>
     <title>
      <p>3.8.46 <code>readslow.c</code></p>
     </title>
     <p><code>/* readslow: keep reading, waiting for more */</code></p>
     <p><code>#define SIZE 512 /* arbitrary */</code></p>
     <empty-line/>
     <p><code>main() {</code></p>
     <p><code> char buf[SIZE];</code></p>
     <p><code> int n;</code></p>
     <empty-line/>
     <p><code> for (;;) {</code></p>
     <p><code>  while ((n = read(0, buf, sizeof buf)) &gt; 0)</code></p>
     <p><code>   write(1, buf, n);</code></p>
     <p><code>  sleep(10);</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.47 <code>replace</code></p>
     </title>
     <p><code># replace: replace str1 in files with str2, in place</code></p>
     <empty-line/>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <empty-line/>
     <p><code>case $# in</code></p>
     <p><code>0|1|2) echo 'Usage: replace str1 str2 files' 1&gt;&amp;2; exit 1</code></p>
     <p><code>esac</code></p>
     <empty-line/>
     <p><code>left="$1"; right="$2"; shift; shift</code></p>
     <empty-line/>
     <p><code>for i</code></p>
     <p><code>do</code></p>
     <p><code> overwrite $i sed "s@$left@$right@g" $i</code></p>
     <p><code>done</code></p>
    </section>
    <section>
     <title>
      <p>3.8.48 <code>signaltest.c</code></p>
     </title>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;signal.h&gt;</code></p>
     <p><code>#include &lt;errno.h&gt;</code></p>
     <empty-line/>
     <p><code>extern int errno;</code></p>
     <empty-line/>
     <p><code>main() {</code></p>
     <p><code> int с, n;</code></p>
     <p><code> char buf[100];</code></p>
     <p><code> int onintr();</code></p>
     <empty-line/>
     <p><code> signal(SIGINT, onintr);</code></p>
     <p><code> for (;;) {</code></p>
     <p><code>  n = read(0, buf, 100);</code></p>
     <p><code>  if (n &gt; 0)</code></p>
     <p><code>   printf(buf);</code></p>
     <p><code>  else {</code></p>
     <p><code>   if (errno == EINTR) {</code></p>
     <p><code>    errno = 0;</code></p>
     <p><code>    printf("interrupt side %d\n", n);</code></p>
     <p><code>   } else {</code></p>
     <p><code>    printf("true end of file %d\n", n);</code></p>
     <p><code>   }</code></p>
     <p><code>  }</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>onintr() {</code></p>
     <p><code> signal(SIGINT, onintr);</code></p>
     <p><code> printf("interrupt\n");</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.49 <code>spname.c</code></p>
     </title>
     <p><code>/* spname: return correctly spelled filename */</code></p>
     <p><code>/*</code></p>
     <p><code> * spname(oldname, newname) char *oldname, *newname;</code></p>
     <p><code> * returns -1 if no reasonable match to oldname,</code></p>
     <p><code> * 0 if exact match,</code></p>
     <p><code> * 1 if corrected.</code></p>
     <p><code> * stores corrected name in newname.</code></p>
     <p><code> */</code></p>
     <empty-line/>
     <p><code>#include &lt;sys/types.h&gt;</code></p>
     <p><code>#include &lt;sys/dir.h&gt;</code></p>
     <empty-line/>
     <p><code>spname(oldname, newname)</code></p>
     <p><code> char *oldname, *newname;</code></p>
     <p><code>{</code></p>
     <p><code> char *p, guess[DIRSIZ+1], best[DIRSIZ+1];</code></p>
     <p><code> char *new = newname, *old = oldname;</code></p>
     <empty-line/>
     <p><code> for (;;) {</code></p>
     <p><code>  while (*old == '/') /* skip slashes */</code></p>
     <p><code>   *new++ = *old++;</code></p>
     <p><code>  *new = '\0';</code></p>
     <p><code>  if (*old == '\0') /* exact or corrected */</code></p>
     <p><code>   return strcmp(oldname, newname) != 0;</code></p>
     <p><code>  p = guess; /* copy next component into guess */</code></p>
     <p><code>  for ( ; *old != '/' &amp;&amp; *old != '\0'; old++)</code></p>
     <p><code>   if (p &lt; guess+DIRSIZ)</code></p>
     <p><code>  *p++ = *old;</code></p>
     <p><code>  *p = '\0';</code></p>
     <p><code>  if (mindist(newname, guess, best) &gt;= 3)</code></p>
     <p><code>   return -1; /* hopeless */</code></p>
     <p><code>  for (p = best; *new = *p++; ) /* add to end */</code></p>
     <p><code>   new++; /* of newname */</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>mindist(dir, guess, best) /* search dir for guess */</code></p>
     <p><code> char *dir, *guess, *best;</code></p>
     <p><code>{</code></p>
     <p><code> /* set best, return distance 0..3 */</code></p>
     <p><code> int d, nd, fd;</code></p>
     <p><code> struct {</code></p>
     <p><code>  ino_t ino;</code></p>
     <p><code>  char name[DIRSIZ+1]; /* 1 more than in dir.h */</code></p>
     <p><code> } nbuf;</code></p>
     <empty-line/>
     <p><code> nbuf.name[DIRSIZ] = '\0'; /* +1 for terminal '\0' */</code></p>
     <p><code> if (dir[0] == '\0') /* current directory */</code></p>
     <p><code>  dir = ".";</code></p>
     <p><code> d = 3; /* minimum distance */</code></p>
     <p><code> if ((fd=open(dir, 0)) == -1)</code></p>
     <p><code>  return d;</code></p>
     <p><code> while (read(fd, (char*)&amp;nbuf, sizeof(struct direct)) &gt; 0)</code></p>
     <p><code>  if (nbuf.ino) {</code></p>
     <p><code>   nd = spdist(nbuf.name, guess);</code></p>
     <p><code>   if (nd &lt;= d &amp;&amp; nd != 3) {</code></p>
     <p><code>    strcpy(best, nbuf.name);</code></p>
     <p><code>    d = nd;</code></p>
     <p><code>    if (d == 0) /* exact match */</code></p>
     <p><code>    break;</code></p>
     <p><code>   }</code></p>
     <p><code>  }</code></p>
     <p><code> close(fd);</code></p>
     <p><code> return d;</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>/* spdist: return distance between two names */</code></p>
     <p><code>/*</code></p>
     <p><code> * very rough spelling metric:</code></p>
     <p><code> * 0 if the strings are identical</code></p>
     <p><code> * 1 if two chars are transposed</code></p>
     <p><code> * 2 if one char wrong, added or deleted</code></p>
     <p><code> * 3 otherwise</code></p>
     <p><code> */</code></p>
     <p><code>#define EQ(s,t) (strcmp(s,t) == 0)</code></p>
     <empty-line/>
     <p><code>spdist(s, t)</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> while (*s++ == *t)</code></p>
     <p><code>  if (*t++ == '\0')</code></p>
     <p><code>   return 0; /* exact match */</code></p>
     <p><code> if (*--s) {</code></p>
     <p><code>  if (*t) {</code></p>
     <p><code>   if (s[1] &amp;&amp; t[1] &amp;&amp; *s == t[1] &amp;&amp; *t == s[1] &amp;&amp; EQ(s+2, t+2))</code></p>
     <p><code>    return 1; /* transposition */</code></p>
     <p><code>   if (EQ(s+1, t+1))</code></p>
     <p><code>    return 2; /* 1 char mismatch */</code></p>
     <p><code>  }</code></p>
     <p><code>  if (EQ(s+1, t))</code></p>
     <p><code>   return 2; /* extra character */</code></p>
     <p><code> }</code></p>
     <p><code> if (*t &amp;&amp; EQ(s, t+1))</code></p>
     <p><code>  return 2; /* missing character */</code></p>
     <p><code> return 3;</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.50 <code>strindex.c</code></p>
     </title>
     <p><code>strindex(s, t) /* return index of t in s, -1 if none */</code></p>
     <p><code> char *s, *t;</code></p>
     <p><code>{</code></p>
     <p><code> int i, n;</code></p>
     <empty-line/>
     <p><code> n = strlen(t);</code></p>
     <p><code> for (i = 0; s[i] != '\0'; i++)</code></p>
     <p><code>  if (strncmp(s+i, t, n) == 0)</code></p>
     <p><code>   return i;</code></p>
     <p><code> return -1;</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.51 <code>sv.c</code></p>
     </title>
     <p><code>/* sv: save new files */</code></p>
     <empty-line/>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;sys/types.h&gt;</code></p>
     <p><code>#include &lt;sys/dir.h&gt;</code></p>
     <p><code>#include &lt;sys/stat.h&gt;</code></p>
     <empty-line/>
     <p><code>char *progname;</code></p>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> int i;</code></p>
     <p><code> struct stat stbuf;</code></p>
     <p><code> char *dir = argv[argc-1];</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> if (argc &lt;= 2)</code></p>
     <p><code>  error ("Usage: %s files... dir", progname);</code></p>
     <p><code> if (stat(dir, festbuf) == -1)</code></p>
     <p><code>  error("can't access directory %s", dir);</code></p>
     <p><code> if ((stbuf.st_mode &amp; S_IFMT) != S_IFDIR)</code></p>
     <p><code>  error("%s is not a directory", dir);</code></p>
     <p><code> for (i = 1; i &lt; argc-1; i++)</code></p>
     <p><code>  sv(argv[i], dir);</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>sv(file, dir) /* save file in dir */</code></p>
     <p><code> char *file, *dir;</code></p>
     <p><code>{</code></p>
     <p><code> struct stat sti, sto;</code></p>
     <p><code> int fin, fout, n;</code></p>
     <p><code> char target[BUFSIZ], buf[BUFSIZ], *index();</code></p>
     <empty-line/>
     <p><code> sprintf(target, "%s/%s", dir, file);</code></p>
     <p><code> if (index(file, '/') != NULL) /* strchr() in some systems */</code></p>
     <p><code>  error("won't handle '/'s in %s", file);</code></p>
     <p><code> if (stat(file, &amp;sti) == -1)</code></p>
     <p><code>  error("can't stat %s", file);</code></p>
     <p><code> if (stat(target, &amp;sto) == -1) /* target not present */</code></p>
     <p><code>  sto.st_mtime = 0; /* so make it look old */</code></p>
     <p><code> if (sti.st_mtime &lt; sto.st_mtime) /* target is newer */</code></p>
     <p><code>  fprintf(stderr, "%s: %s not copied\n", progname, file);</code></p>
     <p><code> else if ((fin = open(file, 0)) == -1)</code></p>
     <p><code>  error("can't open file %s", file);</code></p>
     <p><code> else if ((fout = creat(target, sti.st_mode)) == -1)</code></p>
     <p><code>  error("can't create %s", target);</code></p>
     <p><code> while ((n = read(fin, buf, sizeof buf)) &gt; 0)</code></p>
     <p><code>  if (write(fout, buf, n) != n)</code></p>
     <p><code>   error("error writing %s", target);</code></p>
     <p><code> close(fin);</code></p>
     <p><code> close(fout);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>#include "error.c"</code></p>
    </section>
    <section>
     <title>
      <p>3.8.52 <code>system1.c</code></p>
     </title>
     <p><code>#include &lt;signal.h&gt;</code></p>
     <empty-line/>
     <p><code>system(s) /* run command line s */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> int status, pid, w, tty;</code></p>
     <p><code> int (*istat)(), (*qstat)();</code></p>
     <empty-line/>
     <p><code> ...</code></p>
     <p><code> if ((pid = fork()) == 0) {</code></p>
     <p><code>  ...</code></p>
     <p><code>  execlp("sh", "sh", "-c", s, (char*)0);</code></p>
     <p><code>  exit(127);</code></p>
     <p><code> }</code></p>
     <p><code> ...</code></p>
     <p><code> istat = signal(SIGINT, SIG_IGN);</code></p>
     <p><code> qstat = signal(SIGQUIT, SIG_IGN);</code></p>
     <p><code> while ((w = wait(&amp;status)) != pid &amp;&amp; w != -1)</code></p>
     <p><code>  ;</code></p>
     <p><code> if (w == -1)</code></p>
     <p><code>  status = -1;</code></p>
     <p><code> signal(SIGINT, istat);</code></p>
     <p><code> signal(SIGQUIT, qstat);</code></p>
     <p><code> return status;</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.53 <code>system.c</code></p>
     </title>
     <p><code>/*</code></p>
     <p><code> * Safer version of system for interactive programs</code></p>
     <p><code> */</code></p>
     <empty-line/>
     <p><code>#include &lt;signal.h&gt;</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <empty-line/>
     <p><code>system(s) /* run command line s */</code></p>
     <p><code> char *s;</code></p>
     <p><code>{</code></p>
     <p><code> int status, pid, w, tty;</code></p>
     <p><code> int (*istat)(), (*qstat)();</code></p>
     <p><code> extern char *progname;</code></p>
     <empty-line/>
     <p><code> fflush(stdout);</code></p>
     <p><code> tty = open("/dev/tty", 2);</code></p>
     <p><code> if (tty == -1) {</code></p>
     <p><code>  fprintf (stderr, "%s: can't open /dev/tty\n", progname);</code></p>
     <p><code>  return -1;</code></p>
     <p><code> }</code></p>
     <p><code> if ((pid = fork()) == 0) {</code></p>
     <p><code>  close(0);</code></p>
     <p><code>  dup(tty);</code></p>
     <p><code>  close(1);</code></p>
     <p><code>  dup(tty);</code></p>
     <p><code>  close(2);</code></p>
     <p><code>  dup(tty);</code></p>
     <p><code>  close(tty);</code></p>
     <p><code>  execlp("sh", "sh", "-c", s, (char*)0);</code></p>
     <p><code>  exit(127);</code></p>
     <p><code> }</code></p>
     <p><code> close(tty);</code></p>
     <p><code> istat = signal(SIGINT, SIG_IGN);</code></p>
     <p><code> qstat = signal(SIGQUIT, SIG_IGN);</code></p>
     <p><code> while ((w = wait(&amp;status)) != pid &amp;&amp; w != -1)</code></p>
     <p><code>  ;</code></p>
     <p><code> if (w == -1)</code></p>
     <p><code>  status = -1;</code></p>
     <p><code> signal(SIGINT, istat);</code></p>
     <p><code> signal(SIGQUIT, qstat);</code></p>
     <p><code> return status;</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.54 <code>timeout.c</code></p>
     </title>
     <p><code>/* timeout: set time limit on a process */</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;signal.h&gt;</code></p>
     <empty-line/>
     <p><code>int pid; /* child process id */</code></p>
     <p><code>char *progname;</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> int sec = 10, status, onalarm();</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> if (argc &gt; 1 &amp;&amp; argv[1][0] == '-') {</code></p>
     <p><code>  sec = atoi(&amp;argv[1][1]);</code></p>
     <p><code>  argc--;</code></p>
     <p><code>  argv++;</code></p>
     <p><code> }</code></p>
     <p><code> if (argc &lt; 2)</code></p>
     <p><code>  error("Usage: %s [-10] command", progname);</code></p>
     <p><code> if ((pid=fork()) == 0) {</code></p>
     <p><code>  execvp(argv[1], &amp;argv[1]);</code></p>
     <p><code>  error("couldn't start %s", argv[1]);</code></p>
     <p><code> }</code></p>
     <p><code> signal(SIGALRM, onalarm);</code></p>
     <p><code> alarm(sec);</code></p>
     <p><code> if (wait(&amp;status) == -1 || (status &amp; 0177) != 0)</code></p>
     <p><code>  error("%s killed", argv[1]);</code></p>
     <p><code> exit((status &gt;&gt; 8) &amp; 0377);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>onalarm() /* kill child when alarm arrives */</code></p>
     <p><code>{</code></p>
     <p><code> kill(pid, SIGKILL);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>#include "error.c"</code></p>
    </section>
    <section>
     <title>
      <p>3.8.55 <code>toolong</code></p>
     </title>
     <p><code>length($0) &gt; 72 { print "Line", NR, "too long:", substr($0,1,60) }</code></p>
    </section>
    <section>
     <title>
      <p>3.8.56 <code>ttyin1.c</code></p>
     </title>
     <p><code>ttyin() /* process response from /dev/tty (version 1) */</code></p>
     <p><code>{</code></p>
     <p><code> char buf[BUFSIZ];</code></p>
     <p><code> FILE *efopen();</code></p>
     <p><code> static FILE *tty = NULL;</code></p>
     <empty-line/>
     <p><code> if (tty == NULL)</code></p>
     <p><code>  tty = efopen("/dev/tty", "r");</code></p>
     <p><code> if (fgets(buf, BUFSIZ, tty) == NULL || buf[0] == 'q')</code></p>
     <p><code>  exit(0);</code></p>
     <p><code> else /* ordinary line */</code></p>
     <p><code>  return buf[0];</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.57 <code>ttyin2.c</code></p>
     </title>
     <p><code>ttyin() /* process response from /dev/tty (version 2) */</code></p>
     <p><code>{</code></p>
     <p><code> char buf[BUFSIZ];</code></p>
     <p><code> FILE *efopen();</code></p>
     <p><code> static FILE *tty = NULL;</code></p>
     <empty-line/>
     <p><code> if (tty == NULL)</code></p>
     <p><code>  tty = efopen("/dev/tty", "r");</code></p>
     <p><code> for (;;) {</code></p>
     <p><code>  if (fgets(buf,BUFSIZ,tty) == NULL || buf[0] == 'q')</code></p>
     <p><code>  exit(0);</code></p>
     <p><code>  else if (buf[0] == '!') {</code></p>
     <p><code>   system(buf+1); /* BUG here */</code></p>
     <p><code>   printf("!\n");</code></p>
     <p><code>  }</code></p>
     <p><code>  else /* ordinary line */</code></p>
     <p><code>   return buf[0];</code></p>
     <p><code> }</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>#include "system.c"</code></p>
    </section>
    <section>
     <title>
      <p>3.5.58 <code>vis1.c</code></p>
     </title>
     <p><code>/* vis: make funny characters visible (version 1) */</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;ctype.h&gt;</code></p>
     <empty-line/>
     <p><code>main() {</code></p>
     <p><code> int c;</code></p>
     <empty-line/>
     <p><code> while ((c = getchar()) != EOF)</code></p>
     <p><code>  if (isascii(c) &amp;&amp;</code></p>
     <p><code>   (isprint(c) || c=='\n' || c=='\t' || c==' '))</code></p>
     <p><code>   putchar(c);</code></p>
     <p><code>  else</code></p>
     <p><code>   printf("\\%03o", c);</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.5.59 <code>vis2.c</code></p>
     </title>
     <p><code>/* vis: make funny characters visible (version 2) */</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;ctype.h&gt;</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> int с, strip = 0;</code></p>
     <empty-line/>
     <p><code> if (argc &gt; 1 &amp;&amp; strcmp(argv[1] , "-s") == 0)</code></p>
     <p><code>  strip = 1;</code></p>
     <p><code> while ((c = getchar()) != EOF) if (isascii(c) &amp;&amp;</code></p>
     <p><code>  (isprint(c) || c=='\n' || c=='\t' || c==' '))</code></p>
     <p><code>  putchar(c);</code></p>
     <p><code> else if (!strip)</code></p>
     <p><code>  printf("\\%03o", c);</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.60 <code>vis3.c</code></p>
     </title>
     <p><code>/* vis: make funny characters visible (version 3) */</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;ctype.h&gt;</code></p>
     <empty-line/>
     <p><code>int strip = 0; /* 1 =&gt; discard special characters */</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> int i;</code></p>
     <p><code> FILE *fp;</code></p>
     <empty-line/>
     <p><code> while (argc &gt; 1 &amp;&amp; argv[1][0] == '-') {</code></p>
     <p><code>  switch (argv[1][1]) {</code></p>
     <p><code>  case 's': /* -s: strip funny chars */</code></p>
     <p><code>   strip = 1;</code></p>
     <p><code>   break;</code></p>
     <p><code>  default:</code></p>
     <p><code>   fprintf(stderr, "%s: unknown arg %s\n",</code></p>
     <p><code>    argv[0], argv[1]);</code></p>
     <p><code>   exit(1);</code></p>
     <p><code>  }</code></p>
     <p><code>  argc--;</code></p>
     <p><code>  argv++;</code></p>
     <p><code> }</code></p>
     <p><code> if (argc == 1)</code></p>
     <p><code>  vis(stdin);</code></p>
     <p><code> for (i = 1; i &lt; argc; i++)</code></p>
     <p><code>  if ((fp=fopen(argv[i], "r")) == NULL) {</code></p>
     <p><code>   fprintf(stderr, "%s: can't open %s\n",</code></p>
     <p><code>    argv[0], argv[i]);</code></p>
     <p><code>   exit(1);</code></p>
     <p><code>  } else {</code></p>
     <p><code>   vis(fp);</code></p>
     <p><code>   fclose(fp);</code></p>
     <p><code>  }</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>vis(fp) /* make chars visible in FILE *fp */</code></p>
     <p><code> FILE *fp;</code></p>
     <p><code>{</code></p>
     <p><code> int c;</code></p>
     <empty-line/>
     <p><code> while ((c = getc(fp)) != EOF)</code></p>
     <p><code>  if (isascii(c) &amp;&amp;</code></p>
     <p><code>   (isprint(c) || c=='\n' || c=='\t' || c==' '))</code></p>
     <p><code>   putchar(c);</code></p>
     <p><code>  else if (!strip)</code></p>
     <p><code>   printf("\\%03o", с);</code></p>
     <p><code>}</code></p>
    </section>
    <section>
     <title>
      <p>3.8.61 <code>waitfile.c</code></p>
     </title>
     <p><code>/* waitfile: wait until file stops changing */</code></p>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;sys/types.h&gt;</code></p>
     <p><code>#include &lt;sys/stat.h&gt;</code></p>
     <empty-line/>
     <p><code>char *progname;</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> int fd;</code></p>
     <p><code> struct stat stbuf;</code></p>
     <p><code> time_t old_time = 0;</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> if (argc &lt; 2)</code></p>
     <p><code>  error("Usage: %s filename [cmd]", progname);</code></p>
     <p><code> if ((fd = open(argv[1], 0)) == -1)</code></p>
     <p><code>  error("can't open %s", argv[1]);</code></p>
     <p><code> fstat(fd, &amp;stbuf);</code></p>
     <p><code> while (stbuf.st_mtime != old_time) {</code></p>
     <p><code>  old_time = stbuf.st_mtime;</code></p>
     <p><code>  sleep(60);</code></p>
     <p><code>  fstat(fd, &amp;stbuf);</code></p>
     <p><code> }</code></p>
     <p><code> if (argc == 2) { /* copy file */</code></p>
     <p><code>  execlp("cat", "cat", argv[1], (char*)0);</code></p>
     <p><code>  error("can't execute cat %s", argv[1]);</code></p>
     <p><code> } else { /* run process */</code></p>
     <p><code>  execvp(argv[2], &amp;argv[2]);</code></p>
     <p><code>  error("can't execute %s", argv[2]);</code></p>
     <p><code> }</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
     <empty-line/>
     <p><code>#include "error.c"</code></p>
    </section>
    <section>
     <title>
      <p>3.8.62 <code>watchfor</code></p>
     </title>
     <p><code># watchfor: watch for someone to log in</code></p>
     <empty-line/>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <empty-line/>
     <p><code>case $# in</code></p>
     <p><code>0) echo 'Usage: watchfor person' 1&gt;&amp;2; exit 1</code></p>
     <p><code>esac</code></p>
     <empty-line/>
     <p><code>until who | egrep "$1"</code></p>
     <p><code>do</code></p>
     <p><code> sleep 60</code></p>
     <p><code>done</code></p>
    </section>
    <section>
     <title>
      <p>3.8.63 <code>watchwho</code></p>
     </title>
     <p><code># watchwho: watch who logs in and out</code></p>
     <empty-line/>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <p><code>new=/tmp/wwho1.$$</code></p>
     <p><code>old=/tmp/wwho2.$$</code></p>
     <p><code>&gt; $old # create an empty file</code></p>
     <empty-line/>
     <p><code>while : # loop forever</code></p>
     <p><code>do</code></p>
     <p><code> who &gt;$new</code></p>
     <p><code> diff $old $new</code></p>
     <p><code> mv $new $old</code></p>
     <p><code> sleep 60</code></p>
     <p><code>done | awk '/&gt;/ { $1 = "in: "; print }</code></p>
     <p><code>            /&lt;/ { $1 = "out: "; print }'</code></p>
    </section>
    <section>
     <title>
      <p>3.8.64 <code>which1</code></p>
     </title>
     <p><code># which cmd: which cmd in PATH is executed, version 1</code></p>
     <empty-line/>
     <p><code>case $# in</code></p>
     <p><code>0) echo 'Usage: which command' 1&gt;&amp;2; exit 2</code></p>
     <p><code>esac</code></p>
     <p><code>for i in `echo $PATH | sed 's/^:/.:/</code></p>
     <p><code>                            s/::/:.:/g</code></p>
     <p><code>                            s/:$/:./</code></p>
     <p><code>                            s/:/ /g'`</code></p>
     <p><code>do</code></p>
     <p><code> if test -f $i/$1 # use test -x if you can</code></p>
     <p><code> then</code></p>
     <p><code>  echo $i/$1</code></p>
     <p><code>  exit 0 # found it</code></p>
     <p><code> fi</code></p>
     <p><code>done</code></p>
     <p><code>exit 1 # not found</code></p>
    </section>
    <section>
     <title>
      <p>3.8.65 <code>which1.H</code></p>
     </title>
     <p><code># which cmd: which cmd in PATH is executed, version 1</code></p>
     <empty-line/>
     <p><code>case $# in</code></p>
     <p><code>0) echo 'Usage: which command' 1&gt;&amp;2; exit 2</code></p>
     <p><code>esac</code></p>
     <p><code>for i in `echo $PATH | sed 's/^:/.:/</code></p>
     <p><code>                            s/::/:.:/g</code></p>
     <p><code>                            s/:$/:./</code></p>
     <p><code>                            s/:/ /g'`</code></p>
     <p><code>do</code></p>
     <p><code> if test -f $i/$1 # use test -x if you can</code></p>
     <p><code> then</code></p>
     <p><code>  echo $i/$1</code></p>
     <p><code>  exit 0 # found it</code></p>
     <p><code> fi</code></p>
     <p><code>done</code></p>
     <p><code>exit 1 # not found</code></p>
     <p><code>@@@ Fri Oct 14 14:21:11 EDT 1983 original version</code></p>
    </section>
    <section>
     <title>
      <p>3.8.66 <code>which2</code></p>
     </title>
     <p><code># which cmd: which cmd in PATH is executed, final version</code></p>
     <empty-line/>
     <p><code>opath=$PATH PATH=/bin:/usr/bin</code></p>
     <empty-line/>
     <p><code>case $# in</code></p>
     <p><code>0) echo 'Usage: which command' 1&gt;&amp;2; exit 2</code></p>
     <p><code>esac</code></p>
     <p><code>for i in `echo $opath | sed 's/^:/.:/</code></p>
     <p><code>                             s/::/:.:/g</code></p>
     <p><code>                             s/:$/:./</code></p>
     <p><code>                             s/:/ /g'`</code></p>
     <p><code>do</code></p>
     <p><code> if test -f $i/$1 # this is /bin/test</code></p>
     <p><code> then # or /usr/bin/test only</code></p>
     <p><code>  echo $i/$1</code></p>
     <p><code>  exit 0 # found it</code></p>
     <p><code> fi</code></p>
     <p><code>done</code></p>
     <p><code>exit 1 # not found</code></p>
    </section>
    <section>
     <title>
      <p>3.8.67 <code>wordfreq</code></p>
     </title>
     <p><code>awk ' { for (i = 1; i &lt;= NF; i++) num[$i]++ }</code></p>
     <p><code>END {for (word in num) print word, num[word] }</code></p>
     <p><code>' $*</code></p>
    </section>
    <section>
     <title>
      <p>3.8.68 <code>zap1</code></p>
     </title>
     <p><code># zap pattern: kill all processes matching pattern</code></p>
     <p><code># BUG in this version</code></p>
     <empty-line/>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <empty-line/>
     <p><code>case $# in</code></p>
     <p><code>0) echo 'Usage: zap pattern' 1&gt;&amp;2; exit 1</code></p>
     <p><code>esac</code></p>
     <empty-line/>
     <p><code>kill `pick \`ps -ag | grep "$*"\` | awk '{print $1}'`</code></p>
    </section>
    <section>
     <title>
      <p>3.8.69 <code>zap2</code></p>
     </title>
     <p><code># zap pat: kill all processes matching pat</code></p>
     <p><code># final version</code></p>
     <empty-line/>
     <p><code>PATH=/bin:/usr/bin</code></p>
     <empty-line/>
     <p><code>IFS='</code></p>
     <p><code>' # just a newline</code></p>
     <p><code>case $1 in</code></p>
     <p><code>"") echo 'Usage: zap [-2] pattern' 1&gt;&amp;2; exit 1 ;;</code></p>
     <p><code>-*) SIG=$1; shift</code></p>
     <p><code>esac</code></p>
     <empty-line/>
     <p><code>echo ' PID TTY TIME CMD'</code></p>
     <p><code>kill $SIG `pick \`ps -ag | egrep "$*"|` | awk '{print $1}'`</code> </p>
    </section>
    <section>
     <title>
      <p>3.8.70 <code>zap.c</code></p>
     </title>
     <p><code>/* zap: interactive process killer */</code></p>
     <empty-line/>
     <p><code>#include &lt;stdio.h&gt;</code></p>
     <p><code>#include &lt;signal.h&gt;</code></p>
     <p><code>char *progname; /* program name for error message */</code></p>
     <p><code>char *ps = "ps -ag"; /* system dependent */</code></p>
     <empty-line/>
     <p><code>main(argc, argv)</code></p>
     <p><code> int argc;</code></p>
     <p><code> char *argv[];</code></p>
     <p><code>{</code></p>
     <p><code> FILE *fin, *popen();</code></p>
     <p><code> char buf[BUFSIZ];</code></p>
     <p><code> int pid;</code></p>
     <empty-line/>
     <p><code> progname = argv[0];</code></p>
     <p><code> if ((fin = popen(ps, "r")) == NULL) {</code></p>
     <p><code>  fprintf (stderr, "%s: can't run %s\n", progname, ps);</code></p>
     <p><code>  exit(1);</code></p>
     <p><code> }</code></p>
     <p><code> fgets(buf, sizeof buf, fin); /* get header line */</code></p>
     <p><code> fprintf (stderr, "%s", buf);</code></p>
     <p><code> while (fgets(buf, sizeof buf, fin) != NULL)</code></p>
     <p><code>  if (argc == 1 || strindex(buf, argv[1]) &gt;= 0) {</code></p>
     <p><code>   buf[strlen(buf)-1] = '\0'; /* suppress \n */</code></p>
     <p><code>   fprintf (stderr, "%s? ", buf);</code></p>
     <p><code>   if (ttyin() == 'y') {</code></p>
     <p><code>    sscanf(buf, "%d", &amp;pid);</code></p>
     <p><code>    kill(pid, SIGKILL);</code></p>
     <p><code>   }</code></p>
     <p><code>  }</code></p>
     <p><code> exit(0);</code></p>
     <p><code>}</code></p>
     <p><code>#include "ttyin2.c"</code></p>
     <p><code>#include "strindex.c"</code></p>
     <p><code>#include "efopen.c"</code></p>
    </section>
   </section>
  </section>
 </body>
 <body name="notes">
  <title>
   <p>Примечания</p>
  </title>
  <section id="n1">
   <title>
    <p>1</p>
   </title>
   <p>Система UNIX представляет собой продукт BELL Laboratories. Слово "UNIX" не акроним: оно перекликается со словом "MULTICS" — названием операционной системы, с которой работали К. Томпсон и Д. Ритчи ранее.</p>
  </section>
  <section id="n2">
   <title>
    <p>2</p>
   </title>
   <p>В современных системах такого ограничения нет, длина файла там ограничена в большинстве случаев 255 символами (<emphasis>прим. редактора</emphasis>)</p>
  </section>
  <section id="n3">
   <title>
    <p>3</p>
   </title>
   <p>Порядок, конечно, не строго алфавитный, прописные буквы предшествуют строчным. Чтобы узнать порядок, используемый в программе <code>sort</code>, см. <code>ascii(7)</code>.</p>
  </section>
  <section id="n4">
   <title>
    <p>4</p>
   </title>
   <p>Эта возможность плохо реализована в <code>shell</code>. Просмотр файла после ввода каждой команды сказывается на времени работы системы. Кроме того, если вы достаточно долго работаете в редакторе, то не узнаете о новой почте, поскольку не задаете новых команд в начальном интерпретаторе <code>shell</code>. Лучшим решением была бы проверка через каждые несколько минут, а не после очередной команды. В гл. 5 и 7 будет показано, как реализовать такую проверку получения почты. Третьим решением, хотя и не для всех доступным, может быть извещение, посылаемое самой программой <code>mail</code>, — она точно "знает", когда появится почта.</p>
  </section>
  <section id="n5">
   <title>
    <p>5</p>
   </title>
   <p>В каждом байте файла находится число, достаточно большое, чтобы закодировать изображаемый символ. В большинстве систем UNIX кодировка называется ASCII ("American Standard Code for Information Interchange" — американский стандартный код для обмена информацией), но на некоторых машинах, особенно произведенных фирмой IBM, используется кодировка, называемая EBCDIC ("Extended Binary Coded Decimal Interchange Code" — расширенная двоично закодированная десятичная общая кодировка). Здесь и далее в книге мы будем применять множество символов ASCII; воспользуйтесь командой <code>cat /usr/pub/ascii</code> или прочтите <code>ascii(7)</code>, чтобы узнать восьмеричные значения символов.</p>
  </section>
  <section id="n6">
   <title>
    <p>6</p>
   </title>
   <p>Существует хороший тест на единообразие системы, предложенный Д. МакИлроем. UNIX легко выдерживает его. Можно ли результат компиляции с Фортрана использовать как входной поток для компилятора с Фортрана? Очень больше число систем не позволяет этого сделать.</p>
  </section>
  <section id="n7">
   <title>
    <p>7</p>
   </title>
   <p>Признак установки uid введен Д. Ритчи</p>
  </section>
  <section id="n8">
   <title>
    <p>8</p>
   </title>
   <p>Индексный дескриптор обозначается как inode ("index node") или i-node. — <emphasis>Прим. перев</emphasis>.</p>
  </section>
  <section id="n9">
   <title>
    <p>9</p>
   </title>
   <p>Тем не менее такое различие существует в большинстве операционных систем</p>
  </section>
  <section id="n10">
   <title>
    <p>10</p>
   </title>
   <p>Это старая адресация для UUNET сетей</p>
  </section>
  <section id="n11">
   <title>
    <p>11</p>
   </title>
   <p>Ранняя версия файловой системы UNIX была уничтожена служебной программой, нарушившей это правило, поскольку команда, которая выглядела безобидной, расписала весь диск.</p>
  </section>
  <section id="n12">
   <title>
    <p>12</p>
   </title>
   <p>Позднее будет показано, как обойти эту трудность в командных файлах, где обычно используется команда <code>test</code>.</p>
  </section>
  <section id="n13">
   <title>
    <p>13</p>
   </title>
   <p>Сейчас выпущено как переиздание этой книги, так и новое, третье издание на русском языке</p>
  </section>
  <section id="n14">
   <title>
    <p>14</p>
   </title>
   <p>Однажды К. Томпсона спросили, что бы он хотел изменить, если бы ему пришлось заново конструировать систему UNIX. Он ответил: "Я бы написал <code>creat</code> с е."</p>
  </section>
  <section id="n15">
   <title>
    <p>15</p>
   </title>
   <p>Автор <code>yacc</code> С. Джонсон назвал свою программу "еще одним компилятором компиляторов" (yet another compiler-compiler), поскольку во время ее разработки (1972 г.) уже существовало довольно большое число таких программ, <code>yacc</code> — одна из немногих, получивших признание.</p>
  </section>
  <section id="n16">
   <title>
    <p>16</p>
   </title>
   <p>Сообщение <code>yacc: "shift/reduce conflict"</code> обозначает серьезную проблему и чаще всего является симптомом не неоднозначности, а явной ошибки в грамматике.</p>
  </section>
  <section id="n17">
   <title>
    <p>17</p>
   </title>
   <p>Макроопределения ms находятся в файле <code>/usr/lib/tmac/tmac.s</code>, а макроопределения <code>man</code> в файле <code>/usr/lib/tmac/tmac.an</code></p>
  </section>
  <section id="n18">
   <title>
    <p>18</p>
   </title>
   <p>Программа <code>eqn</code> вряд ли смогла бы появиться, если бы уже не существовала <code>yacc</code>.</p>
  </section>
 </body>
 <binary id="img_0.jpeg" content-type="image/jpeg">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</binary>
 <binary id="img_1.jpeg" content-type="image/jpeg">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
63/6rrnxljji1eGaSEvaNUXTRNGljDWDAUzK0JkivO1XOoerRPjD4TmRgc0+eeVj7zboo3JS
2izSxgP9BzVjn4jraJqOrgLtU1tqe2ONprdjxqmw1LQzIoWdskZblpvSH0T4ydIZDrucM0RZ
pGyMceq/CiFstM1+QDBoGDUykl1zK0r2rwi0XdLc0dG/bLm1IqMwjKJ7QJPvVFfy/A0UNMHt
cf7U+zsfsezfA/8Ax+0MfsWzfA//AB+1naOytLbxFdL6j7luTOd3Lcmc7uW5M53ctyZzu5bk
zndy3JnO7luTOd3Lcmc7uW5M53ctyZzu5bkzndy3JnO7luTOd3Lcmc7uW5M53ctyZzu5bkzn
dyLjYmUArtu5Ne2xso4VHlu5bkzndy3JnO7luTOd3Lcmc7uW5M53ctyZzu5bkzndy3JnO7lu
TOd3Lcmc7uW5M53co5vBGagIppvX9FuTOd3Lcmc7uW5M53ctyZzu5bkzndydI+xsutFT5buW
5M53ctyZzu5bkzndy3JnO7luTOd3Lcmc7uW5M53ctyZzu5bkzndy3JnO7luTOd3Lcmc7uW5M
53ctyZzu5bkzndy3JnO7luTOd3Lcmc7uW5M53ctyZzu5bkzndy3JnO7luTOd3Lcmc7uW5M53
co5aUvtDqebH5j/1HzKX4CoPlt/LzK0fAfOrN8tv5ebH5j/1HzKX4CoPlt/LzK0fAfOrN8tv
5ebGoprv/UfMpAM7pUIIoQwV8yna0EksyHnUDSKERtBH4yx6OHwVpdH2in5oywPY0M61RVCS
V5c+8a4U81is7pRcc6uq3sPZ9kVs+jvfz1T2yaJoaMh1lIyLwcxxkVreUlnhIijj6zx1lHpH
iaF2FXYELTwPaGDP6pkjXt0xHWeFKZn3rpw8bHxRJHI1sRN3LFOtbHtiAF9rKVq32qoDYQMH
PzqfYpbJPRxjrr5VxTA2UCKQ4UbiPWrRT7hW08pjcafX7FetT4rp9FoyUgjforPH2tzKNjnk
0gcKsdSnRf6ctEn8ykrK0NGZu4t9yZZbTcex/VeMFHFZY3a+bwo36TTxOwN/1rTWYtF0VdeG
Kv6TyxqLxbkrQyd4cG/8Kx5jq/n5i2zuLX1ya3IqOXSMeH4Fl3Ae5SWoSaDVvNjug4e1aV4A
dWmGSlMj32djeqA3/wBVS2a0Nbfi9JvamQVZJexaKZ1yWilYHQ5l9MsEyGRtmF70ta6ohBoy
977tH1X+o0ek/wDry6YWltXNk/8AUXlHEyPN5/vU9nfSuTXFQQMoZK/ULR41a1owTHvdhdPW
Uw1akj39HELxvXuqpDpK+SpU9uCdUjrkq0nUprUNfarLR5qM6dim8pSrcKdqitkU/lYtahdk
sSGyGrS1TWa0ikgOV4hR+SJtDtYYk09vROqSMMwpmX8a1uqCKE1LMCW9ijsbZNEwHE16yjDS
4yk43nFxU911dX0UHvdRrSa17FaOq2uSspvnC7X2Yqoy8wY6/h6/ooQCOsnazdl6/YpngNLm
kuu1T7RabQ463UD6BqtZDgGHq1PtVmdfwF2vsxVQahXm1vR61PWo7RKXXYBdb/M7tP5dNpWR
hxvUUQaPJxm9JTsd6vb/AA8rE13vCBjhY0jt7Vdc0OHtV4QNqr8cLQ719HffBG5/rIWi0Edy
tbt1atnjGFMkCLPHUfyoufZ4y45khNY6CMtbkKZJw8Hjo7PVV9kLGu9YC8pC0n1qkUbWe7or
7q/QVTobTE5xc7VDmZfVeRiaz2gLy0TX+0q54NHT4Vcjja1vqCEggZeGKvMgaHZ1VTZo659V
BrRRoyHmG7RcKq+BjjSmIWj0Ed2taXVfihYx1KVAV/waOvuV/wAHjveu6q+DR1rXqoNaKNGS
kaYgLRW7GzOp9qZF6s/f9kV+3z8yT9ZWHn1mZKwObcfgfot2Yt2Yt2Yt2Yt2Yt2Yt2Yt2Yt2
Yt2Yt2Yt2Yp2NFGiZwA6SzMeKtM4qPoVuzFuzFuzFuzFuzFuzFuzFuzFuzFuzFuzFuzFamRM
DW3WYD6+YH5kn6z/ABw88s3wP/x6W0fPf0lk+ePyPS2v4Y/38wPzJP1nz+zfA/8Ax6W0fPf0
lk+ePyPS2v4Y/wB/MKNFBUnz9shGs3AHpXXBS868ff0jbwrcdeHv6V0gGs6lT9lh0zqVyTZG
GrXZHxTLIaNGacISTdzw8XHpTLKaNC0kTqt8Vz3HVaKlaKIuve0U8cxyOdeHZdQcMj/GnRyR
zQ0h+HAe4+NpI2RmP1u9aikdSr21w84bFbXfzAYpkrK+DjAYFPdEcG5koQxucX/CjHIXB3wo
3i4gGjnNaSB9UJI3XmnIq2ZHHP6rQhzr967S6fEghbWjZQ57qepO0V7Vzq2idCwPlkGbY21T
mNvNe3NjxQrQXnX606qBcX3Dk+4bqOLn0F46MXqBaSF1W5K7pHcKDZDJHXLSMIr4ly2GkbzT
t/ZOdZr2hZngUWQuJIFcl4PeN+9d6qDZiRX2IO13aofRra0HtWkidebkpcnYZk5YLRSPIeP5
SgfX4jWPdruya3EpjGl7Xh1Sx7aJkjzRrWCpWkuTaL/5LmqtOH1j9YT5tYMEVxuGJxWmDqR+
t2CMgZOYwcXhmC0kTrzfX45u59lV4HPocM7nuTWCOIsedQlaWJkZaM7ybO0N0pGXYpLO6Fmm
aezIeuqFjnDCCeswFZemFZ9EGumLWhrPWneEtib924ehw6ab+n81F6Wpk5T4DMKTWd2/kodU
dbrJ7Gi7SMdVMxOZVtfZ4aGuLh6GKrEauzkrne8Sx/0/qUjW60rWXritEkMTZHmlS51E21uD
I20oQ11VZmOaKOu/XFNAy0gTQBQOiq6mPYp8T1hgotQdn5KUkCrcQSoi7swHu/jaPlu/JO+Y
rZqgUvfTFWXF3o/mn6oNCPogRqktLi5uJUwq7rZditLoLPWS6aU7MOsnue6tr/3K+LanyYuF
aVGWKi9d9UtJNwsGAzPuUjYLJcs+jdTSPx96/rKtv9f6lI6VwMOLXLR2OyE2Yk1Mz6VUmOOk
/boH4DL/ABVkyOeB+ic0uoSRQetQY9nb71bTgcetX2qLXf2di/rCgt0bzLHdDSHilE2VnVcK
+bEuNAFK+J4c3DL3qDE9XtUjYNE9r/SfmnWlszHfzP7U1okjbGMcjVCzl8LCcHXWnL2LQ6WI
06uqcE6ssZZI6rhQ/wDCdJHNDr9Zt3DxGTxSXZoxq1GCdaJrulc0No3IKSWxOaQ/OJ+X90yW
0OGrlGzIH1qO0GSHUOAoclHEZI2itXm72+xaHSxBzRdDg05KmljdG46wpl7lEGOAdhSoWjlk
ZFDX0MbybE3qtFB/G1aORrrrHA0Kd8xS2mz6N+k+/wBijtImYXDHW7PYmwX4wDtDQ/8AC8HE
kTSMGlrTh7UWiaIsccRQ4J1q0sN6TB4oaURtccsTKuxbd7EL9L3bTxDbbI8B7utGRgUysjAR
/tDL+6jinlaHsOBa1GzSvjY27dvtFS4LROkjdHU5NxTrVZXh18m8x/tXg5kIN/SXgO33Lwcy
sa1outkA1k6ISxugre6ut48LBG3RPze5S2pjawjC/wDSmCuStqFdkMkmFLz31KdExmq8Ufjm
mvbAA5uWKr4M1Nvwg3W3Rj2LRaHyd69dqc0XwxBrjh5sWuyOCMPhEpgrW5ggxgo0ZDzPTG0W
nSD0rw/6QFSaevxJtHaJWMl67QhFE2jR+K//xAArEAEAAQMCAwgDAQEBAAAAAAABEQAhMUFR
YXHwECAwgZGhwfFAsdFQ4WD/2gAIAQEAAT8h8EszWl6v8BRrEUZZMeRHaIfr2wE6/wChgOKm
fzcGMkTEuh5tBc7kCHM29u39HYJRQQf6MVn80nRlPkHmx3HPL/qiUjX8tAlYCmmIY3tIHrd8
+4pTxoJTegg/0xRGhE/KMm/CjnHCaxkQWj9dqhPZkf8AH4j0qE0fTxVJG3506NKCUKEI/wAl
ByUtw5Uhi9IlojwVB2x+XOZhDaJ/Z2R8XZIz6f5oC5Tly/gYqZJ/K6nvSFx0qVZ17DY/z8wz
SIpr38H5UrpqrMy9kkv9GFbNYe9d+Ra0/VSl+qe3En+l/V+YqGWgN1Jl6dnTt+zNw/6cTQsf
kKWUpBi9LYY5VKszL3Onb9nnH+alMGqxX1qvrVfWq+tV9ar61SYfZV9c/tfXP7X1z+19c/tB
H6VfWq+tV9ar61X1qvrVfWq+tV9ar61X1qvrVfWqQ/mr6fX0KlP5f7SufTf2vrn9r65/a+uf
2vrn9r65/a+uf2vrn9phHALiTV8PYf2vrVfWq+tV9ar61X1qvrVfWq+tV9ar61X1qvrVfWq+
tV9ar61X1qvrVfWq+tV9ar61X1qvrVfWq+tV9ar61X1qvrVfWq+tV9ar61X1qmSTup8EgyRF
KOiPiuiPiuiPiuiPiuiPiuiPipKJejSuiviuiviuiviuivioOZejSkOloshWWnA9K6I+K6I+
K6I+K6I+K6I+K6I+Ki6D2p6Ee1Ol1nCugPiuvfikqxCAjGcU9cNUKpdWK6K+K6K+K6K+K6K+
K6K+K6K+K6K+K6K+K6K+K6K+K6K+KAgIoEKWyRfLWQl6NK6I+K6I+K6I+K6I+K6I+K6I+K6I
+K6I+K6I+K6I+K6I+KsYbSIBsHCuiPiuiPiuiPiuiPiuiPiuiPiuiPiuiPiuiPiuiPiuiPio
1aOBJFja9dEfFdEfFdEfFdEfFdEfFdEfFdEfFdEfFdEfFdEfFdEfFdEfFHESwBPCfgqkhTZ7
Qljs6jv2ZDLUetR0GnQqTVpvnu9C2a6Bs8Q+qvXxfTt/8rZKqydqBHQa4SndZenG9/D6Fs10
DZ4l43/b4vp2/wDhbJbhQik7MnueBm7oByBJluCoe76+ITMiiTEjZo5oMCZgCA8MSilBdFnA
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
qR3IGbdo85nIMJ005vLdnkVngcjS9luaYrcbKAybA00zOajx04FDN3DDUCGTxC8q93Xg70+B
SzYWS5liThmp+GQ+CEvm3vSpasCjn3XLxvNcHOO06x3NEVAbt+KHu1PFc3Qt77m5oGfPOmcx
GNXPpUJ6k7CRDhjOc1HgHuT2Zn4p0JqSC7POh00larX0XxQsxcEEWjBaQeBamgK8uTAbwYN+
1cG21A2bParxEMQGYzpBej65I2XNuxy9qz5cl0h3KRr2REGc6XjG3OnUg0nJiL++N6fXIuWT
zqWZeCh6UCYmdVhgpVRy0VZE5oGKyRceGInv2xE6vgDspqh0p2oz6zxY4FAJLAaH4aIk0jZm
mIQiVK9w2wOFeEpOrX71G4v5JxpRc/F0+fYXfP8A2v/aAAgBAQAAABAAH/8A/wD/APz/AP8A
/wD/APgBP/8A/wD8x/8A/wD/AP8AwAn/AP8A/wD/AH//AP8A/wD+AE//AP8A/wBz/wD/AP8A
/wDwAj//AP8A/wD/AP8A/wD/AP8AgBD/AP8A/wD8/wD/AP8A/wD/AACP/wD/AP7v/wD/AP8A
/wD/AIQ//wD/APj/AP8A/wD/AP8A/iH/AP8A/wCX/wD/AP8A/wD/APkH/wD/APT/AP8A/wD/
AP8A/wDof/8A/gf/AP8A/wD/AP8A/wAD/wD/ALAf/wD/AP8A/wD/AAIAADQCAAAAAA/4D0Ab
/wDgAAAAAP8Av4NgIAH/AP8A/wD/APv9/gABAA//AP8A/wD/AN/v8AAAAD/9/wD/AP8A/wB/
EABgD/8A/wD/AD9+9fnsCisB908hOkfD0KB7/t+fudv32b5jNd/J/D3OP/7/APBf/wD/AP8A
/wAf/wD/AP7/ADP/AP8A/v8A/wD/AP8A/wDn/f8A/wD/AP8A/wD/AP8A/wD/AAs//wD/AP8A
/wD/AP8A/wD/APgMigAAAgQAAAP/AMAH/wD+/wD/AJ//AP8A7/4AH/8A9/8A/P8A/wD/AH/w
Ap//AD//APf/AP8A/wD/AIARv/8A/wD/AP8A/wD/AP8A/ACB/wD3/wD/AP8A/wD/AP8A8hwA
7pf/AF3/AP8AiX/1OANOf/aH/wD29/8A6mACVf8AFf8A/wCbv/8A4AANP/d//wD/AHv/AP8A
wAAB/wC4d/8Ag8P/AP8AwAAP/wD/AP8A/wD/AO//AP8AQAD/AP8A/wD/AP8A/wB//wD8gA//
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</binary>
 <binary id="img_2.jpeg" content-type="image/jpeg">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
KMkTnUvA0/usP7tqxeyjdN8ac6xDpTsZamkXSFiZJGguFaermUTqCt+qZPKafZhzupGgArFW
gQ9o+QlsTWlxFOEaIxhkTrjXN+n9FJipy107uZui8NLIqkcS/wBVOhRyYoRsYwaMNSVFPC5r
ZI/xc6MOxiiHOS+tU3BYKFpdZUuQiZh4aNyH2n/StmjDmTOa29p08a73h8v7m+2fl4qLE2xD
ZnS/1/oo2SiKNgNTR1ShhMOxmzoBc52lEMKGxaW3ufXLqXg8VhMhBkfdT4fohG6OO5gAbw9V
LJZE7aHMX/8ASOMxRZd5rWJ2MwmzNRwozlcottHFFCw1LbriU/CYfDM2EZDSSf6KSDYQtDm0
yk/6XgU0VMi9rq65+UOnpwi23vBriRRwcCPGu94fL+5vtn5eSPt895efj3o5KkFldOf0O73h
8v7m+2fl6Iz8ld7w+X9zfbPy9Olsk0bDecnOot6h7QLeoe0C3qHtAt6h7QLeoe0C3qHtAt6h
7QLeoe0C3qHtAt6h7QLeoe0C3qHtAt6h7QLeoe0C3qHtAt6h7QLeoe0C3qHtAt6h7QLeoe0C
3qHtAt6h7QLeoe0C3qHtAt6h7QLeoe0C3qHtAt6h7QLeoe0C3qHtAu5+ylY+jzW11ej07uzF
uzFuzFuzFuzFuzFuzFuzFuzFuzFuzFuzFuzFuzFuzFK+OBrXClD8VGTh2EloW7MW7MW7MW7M
W7MW7MW7MW7MW7MW7MW7MW7MW7MW7MQeyBocND+TJvh/dQ+wPy3N8P7qH2B6Ry9EuaHFtRqE
6KWeU50LS+qhi8ImtrpedPSeXoiTLgv4QUs3QLR6Vy8hz8ljn52m0plRm/hfkjPxFPFvhutu
501n4RT8n5flrX/5Xl5BX8nZeOjjjbdLJxQVG9xbIX6sEeQ+NVh4oGNrI25205gnYS2G5lan
NSF0LTOySw26JuLcWyNdSrCy21NnhDQ5wrR6kdbE0NyTsG1sVw0cQUXR25a1Cbi6QltK0oU2
aSwXczVIG/4djNLo61UsEsYbJHrRMw+yhJeeDmtsGwyNHGABFE3EQ0veQ1rXdNVFAWQl8gHT
kthMGULLmFqODpBVurs1GwtjIeeg6LaujD5dKMGSGMq141MZjpT+q8MLSBbUhPfDHExgyBfV
S4RohuY3Ug5+l8vF1WFnJNmXwzTpCeC0VqmztGrcjTmWI/cmxYZoZJMal9FI6TFyyOyFCcjn
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
tbxAKovGosA2e3P/AAY/L/wGf//Z</binary>
 <binary id="img_3.jpeg" content-type="image/jpeg">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
1ZVrl8vwL+1cvl+Bf2rl8vwL+1cvl+Bf2rl8vwL+1cvl+Bf2rl8vwL+1I0jZnudffz/8x/Ee
YJ628R5/+Y/iPME9beI8/wDzH8R5gnrbxHn8kc6lYdT779HMNnhZsjLdtVBv2VI0zZiG0Nvo
QYb+SbdGnv5yHinARzouTdW1E9prX2YQW9VPYBZk/Sp4JpM4jv6NqXAYd9n+OSo5DKZoGNmD
DUVcbvryQxSGGBBvXjGtiVZsN+Nh2VLFDLsoI+lRq1QiWbaQSacMarUWFwzLtpTxj6NPOmLY
souQ6ixra8W4INvRqeWQ2Gb9aEk8qhb8TJvFQqsg2ch4gWhlF5H4tbdcWzzAZiuUWPZSy7j0
jm0PB9PfSz5xkC9f+1TYqTgo24k1iSWVQ17cLto7cfw83Se351mkjDZrZRfVvVQ6PrzYefgO
+6430sABZjqbeiKxOHlOVjxb9NYfDQcOTXQf166wmJb+VwQzW3WqZtovDWy9tOZwBnJIVja+
lSPth5Re6qWtuoRTOBKumvpVhtVOU8Ihu2ocTFw1TpFbbKuUrZrtur+HTLHc27ebRxRxzMyt
rZDallOGGa1uGmtZbDL1UCsEYI3HLWV1DKeg1mjhVTu8xaWNW9dWjjVPZFWljVvXX2USJ7Iq
zC4q64eMH2aAdFa2uorgwxj1LQKwRgg3FlrM0MZPWVrKosOqs/k8ebfurTnOdt+Zh/vzBZH4
xJ/55/8AmP4jzBPW3iPP/wAx/EeYJ628R5/+Y/iPME9beI83Z1xKKudgBsr7jbrrlkfc/OuW
R9z865ZH3Pzrlkfc/OuWR9z865ZH3Pzrlkfc/OsiYxLXJ/k/OuWR9z865ZH3Pzrlkfc/OuWR
9z865ZH3Pzrlkfc/OuWR9z86V5CC12Fx6/MZI8WmTX+5+dcsj7n51yyPufnXLI+5+dcsj7n5
1yyPufnXLI+5+dcsj7n51HI/GO/z5/zJPEeYD238R5hD/XT58/5kniPMB7b+I8wh/rp8+f8A
Mk8R5gPbfxHmEP8AXT5/Le/CJ/U3+hZIp7RsbZcg0pSRY23HzojvfUm/rP1NoFsMxA1+oVik
2b9DZb2qcYhs2S3QPqNLh5chjFzwQb1tJjds2/6FizZsvT9O3gmyBN65Qb1FJJq7b/oyrya4
6NLfUkONeSJRxRG2WpsHOwfZ34VtTrUcSThhIeCuTt3Vt0xGdl1dcgtW1w77J763Ga1PtJ7R
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</binary>
 <binary id="img_4.jpeg" content-type="image/jpeg">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
2Q==</binary>
 <binary id="img_5.jpeg" content-type="image/jpeg">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
vSugLiSx246zlEGwbmW9GbCoCHb3GUth9ZEAjpQhQwCT/VuN9qWiGbQbTteg7OhEbJrmlYwF
gsyVB61tEHF4xQMSG4R8/dXqICCpqu/nSLYwDbPw36VjjASzBDizO34pe+tsityWZtMHFrmZ
8TLU23q76hFibMw8c8KZ/ur1wSKJjSXEQyQZ2P1QjgEpdigS0R2LAEltYdeOaCUBR0KZYR+4
Szw/m3pFJy2Eysudxjg8KlrsnGq6TufjOK08HHeAsNH9+Jhhsclz+c1p6aIJHStWcUsOAxaP
7dqyQXQwGy8wwceFE2mQ4yNl5ixxJ8IIavizmxtXDpUgohKWvCsjlm73NDW+KyxFiwNG3ds4
qVG7HGF7kXHG/Iq5FhHSyc4vLxoGQoQC5oSbuoW6UPNjACZKxiLdedAftw5SGSb44Z1odQMl
27a/FvrNQHZYkZF1bafdIxhW7rBxuzpja8HNi2CIOlneLUJEZeeV+ZWpByFptKLNr2dN70Ss
AE5gIvx8RRka2HyURTNqVVfFhIRBA43qIC08AYCBiM8ZvRzBHaamL/nx0KTsxeLNyNaCPEIz
sFnfW/8A31ypoTbsmbbx901XoqGU/gv1psqsTtBCTpZ43rd8AVBri3hBMxfyqiylZzwxerGn
dmMtJ8WsrFMENuPKsw9FZ2Dh3y/gVPhEJ1fIk6eGEtTBifBQDc05DjNLtRRkleB4oohtSFF0
qlsZSad7eoEAe5//2gAIAQEAAAAQ/wD/AP8A/wD/AP8A/wD/AO//AP8AwD//AP4qX/8A/wD/
APv/AP8A+g//AP8Abof/AP8A/wDv/wD/AP8Ao/8A/wCf/wD/AP8A/wD7v/8A/wD7/wD/AP8A
3/8A/wD/AP3v/wD/AP8A/wD/AP8Akf8A/wD/AP8Agf8A/wD/AP8A/wD/AM+3/wD/AP8A6H//
AP8A/wD/AP8Az/x//wD/AP0P/wD/AP8A/wD/APP/ANf/AP8A/wDA/wD/AP8A/wD/AOf/AP8A
/wD/AP8A/wAf/wD/AP8A/wD9/wD/AOP/AP8A/wDn/wD/AP8A/wDz/wD/AP8Af/8Av/8A/wD/
AP8A/wD+/wD/AP8A3/8A2/8A/wD/AP8A/wD5/d/v9/8A/NaX/wD/AP8A/wB/R8/9/wD7fOv/
AP8A/wD8/wDPJ/8Af/8ADib/AP8A/wD/AL//AJ//AN//ALgX/wD/AP8A/wC//wD3/wD3/wDf
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
rovKCBOLEJ5oNw15j1P/2Q==</binary>
 <binary id="img_6.jpeg" content-type="image/jpeg">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
Sh6ktjx9wzY0VpXiIvUPkhXEsCCaVuyOuYdYF9u92O7ksi1sdvArQhsD/wBgsRstu4J5xsGP
qErY2rb7YtaPbZfkPL0+5ZYBqGVqGVqGVqGVqGVqGVqGV77Cq4yveaXvsr32V77K99le+yvf
ZXvsr32V77K99le+yvfZXvsqj7NKahlahlahlahlahlahlSl9l8h5YWPGLDwsesLHrCx6wse
sLHrCx6wsesLHrCx6wseqwgCwwNFhwFhwFhwFhwFhwFhwFhwFhwFhwFhwFbDAVWFj1hY9YWP
WFj1hY9YWPWFj0cEOGf5enOP8lbFWnbw0p2pvl+Q8vTnH+ati/W6yn++CX5Dy9Ocf/BW3ttt
p2p4Jmn1G6JpaJpaJpaJpaJpFDWNib+nOP8A4a07V9Laf74ZfkNp3wN/TnH/AMN/rb+vDL8h
tO+BviBCHxMaasaasaasaasaasaasaasaasaasaasaasaasaasaasaasaasaaro43tjzFSOM
qsaasaasaasaasaasaasaasaasaasaaixnh5Dad8Df05x/8ADf6W0/zxS/IbTvgb+na/+j/D
d+6f7Xxy/IbTvgb+n/g23fwVr2orKeSZ+rW/kV+RX5FfkV+RReu0f5FfkV+RX5FfkV+RT84Q
O/0/8FW3ee/9+WX5Dad8DdK8p0/8H0tu89Le3ll+Q2nfA3SvKRThtg2olVqJVaiVVCpaq1Eu
tRLrUS61EutRLrUS61EutRLrUS61EutRLrUS61EutRLrUS61EutRLrUS61EutRLrUS61EutR
LrUS61EutRLotwtyQ2nfA3SvKdP/AAfWle39cvyG074G6V5Tp/4Oyyv9UvyG074G6V5Tp/4O
2le9Lvq+lqUIukCiXmXy5G4W0qSJAueuvsbukyLQgzbiRmDijkMddcWXJEilPyTwbkgW6G00
c+5Hx0i8fdfIuOHWHONmb5fkNp3wN0rynT/wdtlf9U6xVu4G/XEyHZvqB26xtu8pjsTT6el2
LXL+loC6lY0v6r+pJqn1SkoxeMpN33oEC3Vg9Nf/ABxjbtJB+Ps97fL8htO+BuleU6f+Dudc
9ti8+06NYZoOwUIyZY3H220eiW3SiAWyWRQ7Q7axbVHBwmhr3olgh2+KHdcJEbKo1HsMsjRr
AlxEcyQ82Dba7vl+Q2nfA3SvKRB4wouZAWZAWZAWZAWZAWZAVJmP7WSMQ3fm49ZuPWbj1m49
ZuPWbj1m49ZuPWbj1m49ZuPWbj1m49ZuPWbj1m49ZuPWbj1m49ZuPWbj1m49HGjmSG074G6V
5SDYacB0o60o60o60o60o60o6oKN30gy0gy0gy0gy0gy0gy0gy0gy0gy0gy0gy0gy0gy0gy0
gy0gy0gy0gy0gy0gy0gy0gy0gyk2m2pDad8DdK8p05x9adt9P1/NL8htO+BuleU6c4+6nem6
z9XuWN+l11tltjljlFR1u65XO2WVV19tlLbrb6K15tyqq7Zbdvl+Q2nfA3SvKdOcerqf7tsR
AI5dbbaWWutWPNDjMi2JsAVh9EADFXp8dslthhodvt3TAI416cjxXn98vyG074G6V5Tpzj1X
/abbP3/NL8htO+BuleU6c4/0u/eyz/r+aX5Dad8DdK8pEAaoTDLDK6HWJWJWJWJVsR/uGWGW
GWGWGWGWGWGWGWGWGWGWGWGWGWGWGWGWGWGWGWGRYekkNp3wN0rynTnH+lf1ss/6/ml+Q2nf
A3SvKdOcf61/frZ/1suupbbHF1LbWoZ+tUvsqqXUu8kvyG074G6V5Tpzj/W79+tnqdVygI5M
deO8FbWGgxLXhp0q5uyscJaND368GKEbckJJvFl23fVZ4pfkNp3wN0rynTnH+t/79bPU2+9s
N68EpocZ22DAre0BLAXFt3SV1WogC4JiNvuZlDmn5YilO1PFL8htO+BuleU6c4/1v2W/8+B7
3faDANFM8kvyG074G6V5SIyGl7TS7TS7TSupMrtLrtLrtLr6Zpdppdppdppdppdppdppdppd
ppdppdppdppdppdppdppdppdppdppdppdppdppdppdppF63IbTvgbpXlOnOP9b/Wn7/nl+Q2
nfA3SvKdOcf63fv0s/f88vyC79qW3W32+h3wN0rynTnH+tf+vSz9epTpGtZKNvlLDrxxxNca
PHG3EIm8q0oow0Y+QOdEtdZMYYALoaL4py660u6SNpVqy11UIftpqSVqSUQ6S8PuleU6c4/x
V7UQt9v3JKMVKjohgMoRp0AOtCbCzJX/AOwTbd1h5hLNoMAxe0J4pfkK07q8Aa9WWfRZ4ZXl
OnOP9L/16WfvZdbbfboxlbbSy10AV5UYataHDYFVQBK19pv27Y8S27xy/IeSV5SJlGARfuIN
fcQau6gEqs8Is8IqdQB0p9xBr7iDX3EGvuINfcQa+4g19xBr7iDX3EGvuINfcQa+4g19xBr7
iDX3EGvuINfcQa+4g19xBr7iDX3EGvuINfcQa+4g0VIMnn+SV5Tpzj1X9elKd6/0y/IeSV5T
pzj1fX1sp/VL8h5JXlOnOP8AAQcOJW26l9rrtjDQ5bJdqZkBSHUQeMLenyGxmxyWim617UGP
GKuTkgKy/vl+Q8krynTnH319bNt7dl/pWlK0tstsoqN2W3K5uy+qrbS+lLaWUVjdlnpVuy67
fL8h5JXlOnq9o7+6X5DySvKQb7TQWsGWsGVpYq1oq1oq1oq1oq1oq1oq1oq1oq1oq1oq1oq1
oq1oq1oq1oq1oq1oq1oq1oq1oq1oq1oq1oq1oq1oq1oq1oqk3mnpDySvKRAAxQuHBWGAWEj1
hI9YSPWEj1hI9YSPWEj1hI9YSPWEj1hI9YSPWEj1hI9YSPWEj1hI9YSPWEj1hI9YSPWEj1hI
9YSPWEj1hI9YSPWEj0cEOHIeSV5Tp/4Ksp/v9kvyHkleU6f+CqU7U8Pvte5c+1azFlXFtLVD
/WqOWVVLrbvJL8h5JXlOn/g20/3wm0vuCaOCtHdCYuhoMEcgadJubbuAFtFh76HgxQrbkhJt
4wuyv1WeKX5DySvKdP8AwbadqeE766h3u+8yOJfZDCNEtAywFxbd0i5VqIAuCHjbrmZQ1l+V
IpSlKeKX5DySvKdO07hfwPe77QceaKZ5JfkPJK8pFAvki4kxYkxYkxYkxYkxYkxYkxYkxYkx
YkxYkxYkxYkxYkxYkxYkxYkxYkxYkxYkxYkxYkxYkxYkxYkxYkxYkxYkxYkxYkxEivCn+SV5
Tpzj/wC6X5DySvKdOcf/AHS/IJ4lkdWX2u2eGV5Tpzj90obcCJcIVjQ9RUMf33X4Ysk26wi+
TkakXx8mX9Vox7hIgEdc46F6SJeiEFEvJGjyr7nvcecm5W8gR6y2tlN037msvpJX3Cf+S7SK
7SK7SK7SK7SK7SK7SK7SK7SK7SKfgyCH+nOP3Tot74YUkLcA7K+wg3bF05fbbcwwyBLVvj6O
HmNXKaJYdjot6ysZGylT3VMjXlAR8gLSPjGquyFpLX3JPut3FN32O2bpfkE4My8rLKNWeLpz
j99GGaXPMtEW+3Z7dGWqVubsvVjLbaBCbaa07KYGoMdS2lvrUdm67svZbVW7K1pSltN0vyHl
iCiGBcicsicsicsicsicsicsicsicsicsicsicsicsicsicsicsicsicsicsicsicsicsics
icsicsicsicsicsicsicsiciyH35Dy9Ocf8A3S/IeXpzj/7pfkPLAtXtA+A2RZArbX6rSHqD
sBl2GspmSafMRcm0G6iyrQ2BibSmEJItmPJ+TZHL3yjV7h3/AO1//8QAQxAAAAQCAwsKBgIB
BAIDAAAAAAECAwQREjSSEyEwMTIzNXOiweEFECAiQVFhcrHRFCNAcZGhQlKBJEOT8GKyUGOC
/9oACAEBAAY/ArpRNMjkZfXwzRondlUZ92GXrNxfX8ma3enDL1m4vr+TNbvThlIU06ua59RI
q0TYFWibAq0TYFWibAq0TYFWibAq0TYFWibAq0TYFWibAq0TYFXibAq0TYFWirAq0VYFWirA
q0VYFWirAq0VYFWirAq0VYFWirAq0VYFWirAq0TYFWibAqsVYFWirAq0VYFWirAgKLbqKLv8
0y7Swy9ZuLD3/oOTNbvThl63cWBuhtrWX/j2C6kk09kuZMPcjPFNQJRtLXPtLEQS6kjIld+A
pm04sv8A6ynIXVKDTI6JlgOTNbvThnNbuLpXNbD85yLq4wV0YiEz70hDqmXpK7KOL7h5dzWj
qKvLK+EttpW6uZ9XFRCpJUlSMpJhCHIRRuUiJK1J9A7/AI9QyZn2doP4SGN1CbxqNVEKkSkq
QclJVjIHDwzRvOpyr9EiC4daKDqeycwpiGaN5xGVfkRBLES1cXFZPWmR/wCRSdOU8Rd4cUqG
cShSDkZhes3EFJhGLsSMpVOiQW2pJtvIymz6XJmt3pwzmt3F0ieIus3j+wRRPNomq/jV/wBm
IVk1ZPWcv45Yg/MzLq9gmWM1X7wiuokpdx4r4hL59nqHP8eo+XjoHP7Tvj5CmCKl2kYdiFON
qUsj6pXhEF23gbiG0E8ZHLxESZ3zMymc74hjTj7BCTlK92+Ie8hiJo5U1SshdxNoipX6RTMJ
i3XWqSk9ZKelyZrd6cM5rdxdJSFFMlFIXJF+eMz7Q7Q/3Dn9hQJ1SCPHLtBoRFOUTK94C6Nv
q8eqV8XVUSsjI+r4BLSn1EX8r2UDbuqnEdhK7ApcK/ckn/A0zIXR1w3XcVIyxD4iHdNlw8ci
mRi6uum67KRHKUgp6GeNlSsq9MjBPxDxvuJyZlIiFFd4yxKLGQNt2MWafBMvyM+pSf6yvBTs
I+bE8aaM0i7POXVwsm9Ik/bpcma3enDLJS0p+Z2n4EM83aGebtDPN2hnm7QzzdoZ5u0M83aG
dbtC88i0M6i0M6i0M6i0M6i0M6i0M6i0M6i0M6i0M6i0M6i0M6i0M6i0M6i0M6i0M6i0M83a
GebtDPN2hnm7QzzdoZ5u0OTaKiV83sPxThlLfapKJcsZir7ahV9tQq+2oVfbUKvtqFX21Cr7
ahV9tQq+2oVfbULzG0YzG0YzG0YzG0YzG0YzG0YzG0YzG0YzG0YzG0YzG0YzG0YzG0Yq+2oV
fbUKvtqFX21Cr7ahV9tQq+2ocn3BujSdv359pYZzW7i+v5M1u9OGc1u4vr+TNbvThnNbuL6/
k4u9zeQxvf8AMv3GN7/mX7jG9/zL9xje/wCZfuMb3/Mv3Dy0KeJSUGZfOV74BzW7i+v5M1u9
PSiNUr0wDmt3F9fyZrd6elEapXpgFKajFMJpyokQ0o5Y4jSjljiNKOWOI0o5Y4jSjljiNKOW
OI0o5Y4jSjljiNKOWOI0o5Y4jSjljiNKOWOI0o5Y4jSjljiNKOWOI0o5Y4jSjljiNKOWOI0k
uyNJrsDSjljiNKOWOI0o5Y4jSjljiNKOWOI0o5Y4jSjljiNKOWOI0o5Y4jSjljiIC7RSn5u3
plivl0ojVK9MA5rdxfX8ma3enpRGqV6YBet3F9fyZrd6elEapXpgF6zcQv8A13J1GVKnen90
jJhfyoZML+VDJhfyoZML+VDJhfyoP0yh6NzVORnPEMmF/Khkwv5UMmF/Khkwv5UMmF/Khkwv
5UFtKaaM0nK8Zhes3FzS+t5M1u9PSiNUr06cR5gvWbi+v5M1u9PSiNUr06cR5gooeHS4mnjN
UhUm7YqTdsVJu2Ki3bFRbt8RUW7fEVFu3xFRbt8RUW7fEVFu3xFRbt8RUW7fEVFu3xFRbt8R
UW7fEVFu3xFRbt8RUW7fEVFu3xFRbt8RUW7fEVFu3xFRbt8RUW7fEVFu3xFRbt8RUW7fEVFu
3xFRbt8RUW7fEQHxTCWvm3pKnO+XSiNUr06cR5gvWbi+v5M1u9PSiNUr06cR5gvWbi6MvquT
NbvT0ojVK9OnEeYL1m4umdHK7JgoRTCCX29YMNtNJXdJ4zkGmzQSolyXVLEEXdDSiV2ImDU2
glq7jOQ+LOETcsed4D4hbRNtynOlMKXCttXJJ0ZuHfMKhH0Eh5N8qOJQSzcGzpq6h0gj4qHI
m1/yQqchdktpWgsczHxRttkUp5RjNIShOUcwcLCJQpSSOkpeIh8LFoShSs2pJ3lYDkzW709K
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
DIJhkEwyCYZBMMgmGQTDIJhkEwyCYZBMMgmGQTDIJhkEwyCYZBMMgmGQTDIJhkEwyCYZBMMg
mGQQNzoD3jgRBxMB/wCRgPeEEsHTkknHgASQEAw9DeiuDj+BD/AggxCHMwJJPWexODH54N9R
Cwo/LNwB+EYCRh8CnAlapYsJj8JiZ6JiI/KGYzBElB7iuDgWliOAyOYgRoPmWb22A94Q6e7i
E/p6WOASaPAqACUwU3nHAW4T4xBF98BwBSIDwNTVgDpq1YA3B3iE4XCXGIovv22A/QDiAOQE
AAGH8nASRcQAAcTI9FbnlW55T4k2VVbXlW15VteVbXlW15VteVbXlW15VteVbXlW15VteVbX
lW15VteVbXlW15VteVbXlW15VteVbXlW15VteVbXlW15VteVihgyzo84XjQuagwyNVXX1Thm
vqoUbvVVdlVV2VVXZVVdlVV2VVXZVVdlVV2VVXZVVdlVV2VVXZVVdlVV2VVXZVVdlVV2VVXZ
VVdlVV2VVXZVVdlVV2VVXZVVdlVV2VVXZVSQdPLszM1POHu/Acd/LwHu/AaRygdsrmaPYiJk
B2OCaaEaALIIHfh9ElmMzZvwJAS9mh98kcwZBkcj7rAe7oedgOU6GRQCZLFQv59cHqiJvRC7
DoYQk7sDQTyTl/w1NAsCmwq2WYN+yDIiAEDIYs6rENCAXRAcRmChEBzAFuQ2Yzb5onrA4RkZ
w2Rtk7TE+IPc4D3dHUOUItIgEyPmjmxCBCcCCLN+Mh4IFLyJ8C2Tp7C+CSGBi4iwLfCK5PJs
OXWEMPtFTyY+GW7EXQOgKTZCpibig9c/gcJLmMuqH2OKYzhPBkBAAAw9zgPQB+xOgqZixajr
FIjQkXOj3eAiwcieiQBGBGa25O3J25O3J25O3J25O3J25O3J25O3J25O3J25O3J25O3J25O3
J25O3J25O3J25O3J25O3J25O3JglpNldmTmPrnD3j+aYD3j+CYYlEQGj9Id4VwY8se8esakk
qMT9k/DATIA9CRIYZo5AjwZlpQkydVDnxIBUyQIIkOec2GCHMYB8vJxkT0CJEcO5xsT1dsii
eiAGJgS0BCFAbgJSKcldS0MAOjAcW0BxoydbodgPIwMYACEGR5AFsw0CcYyPygEDkcAHB7CE
qZBHBheHBmDAG6VTxk5OLN29cyA4/BCvSSeuPxiCJi98K4SUtMWOqXLup5aRIkSJEiRIkSDC
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</binary>
 <binary id="img_7.jpeg" content-type="image/jpeg">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
NX4f81cKkTElcj/x6TSNuTwA2hLbfQvtMx0QY6IMdEGOiDHRBjogx0QY6IMdEGOiDHRA49Xv
G1IgstY6IMdEGOiDHRBjogx0QY6IMdEGOiDHRBjogmTIy4VVpmyH+noKLf2b7iG77GxRjYox
sUY2KMbFGNijGxRjYoxsUY2KMbFGNijGxRjYoxsUY2KMbFGNijGxRjYoxsUY2KMbFGNiixea
endAvYttdIZZk46IMdEGOiDHRBjogx0QY6IMdEDz8CQ2pEEwy9XsDHRBjogx0QY6IMdEGOiB
6bFNmk0jq32mZVBGVQRlUEZVBGVQRlUEZVBGVQRlUEZVBGVQRlUEZVBGVQRlUEZVBGVQRlUE
ZVBGVQRlUEZVBGVQRlUES66GiFVaZsh/p6Kk9lJabfvcthDLYQy2EMthDLYQy2EMthDLYQy2
EMthDLYQy2EMthDLYQy2EMthDLYQy2EMthDLYQy2EMthDLYQy2EJ0ZmPO50F78kGIxJlZVBG
VQRlUEZVBGVQRlUEZVBGVQRlUEZVBGVQRlUEZVBGVQRlUEZVBGVQQ7WQiZpNI6t9pncTtPqt
M2Q/09JSexX/AEHTU8hDvUtfn86S3FyVPyOxf+PSaR1b7TOc5LBLOSwlYOZGIyMjLEM70Ood
JTzSAb7RBx9poIfacPmnafVaZsh/p6ak9gv+g5j/AMTt3ikw7DEPt2D0tyNON1xyXJXdk7Jw
zNnIefK0NuXMltwmnLCUywy6l9nmtfn8yfzy1PyORSkoJEphw1LSgsSwaSMjSUhkzQ62tVvi
2GKxXHW8j/x6TSOrfaZz3DO60vWWmqyPvfqYWDRB8PqU5FKIg/EMavwj9KltaqthqTNfecr7
WC9ElWPNO0+q0zZD/T055n5SobZDCtjCtjCtgozYwjQwbYwbQwTQwTQwTQwTQwTQwTQwLQ8l
LfiDgHAOAcA4BwGOAwgt/iiS0eFpm33I7UDyrD/9U0+TrtQSSsvEHuLwlD/Q422aDb5rX5/M
kvblqfkclyyy6zavNuw/LKTTVUFM6BZNORKSQUZPhypipTEv9MqtL5H/AI9JpHVvtM57CJLk
z7JqZNinFlv00FySxFq40iApuNMK34v9DKnTRHt1NOE5KYlxo5u2HNO0+q0zZD/T07D/AACi
3ciVbuk5/Scx+5FTNk8guFD1enEMxSbck1zch1hpEZuvR5tkmra81+O1IabqvKShpDaOEhwk
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
AP8A/wD8n4//APf/AL//AOf/AP8A/wD/AP8A/wD/AP4HD/8A/wD/AP8A/wD/AP8A/wD/AP8A
/wD/AP8A/wACP/8A/wD/AP8A/wD/AP8A/wD/AP8A/wDgAAMAP/8A/wD/AP8A/wD/AP8A/wD/
AP8A/wDAAANAf/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A82A//wD/AP8A/wD/AP8A/wD/
AP8A/wD/AP8A/wDzeP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wDzeF//AP8A/wD/AP8A
/wD/AP8A/wD/AP8A/wD/APN6H/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A838P/wD/AP8A
/wD/AP8A/wD/AP8A/wD/AP8A/wDzfg//AP8A/wD/AP8A/wD/AP8A/wD/AP8A+lxzf4P8/wD/
AP8A/wD/AP8A/wD/AP8A/wD4ojN/wfx//wD/AP8A/wD/AP8A/wD/AP8A+MOzf8D4/wD/AP8A
/wD/AP8A/wD/AP8A/wD/AP8A83/wef8A/wD/AP8A/wD/AP8A/wD/AP8Af7/zf/hR/wD/AP8A
/wD/AP8A/wD/AP8A/wBa5DB/+AP/AP8A/wD/AP8A/wD/AP8A/wD/APNPE/8A+gf/AP8A/wD/
AP8A/wD/AP8A/wD/APpfQ/8A/gf/AP8A/wD/AP8A/wD/AP8A/wD7f/8A8/8A/wAP/wD/AP8A
/wD/AP8A/wD/AP8AYDf/APP/AP8Ah/8A/wD/AP8A/wD/AP8A/wD+gCVWc/8A/wCH/wD/AP8A
/wD/AP8A/wD/APwAIfzz/wD/AA//AP8A/wD/AP8A/wD/AP8A+AAgf/P/AP8AD/8A/wD/AP8A
/wD/AP8A/wDwACD/APP/AP8Av/8A/wD/AP8A/wD/AP8A/wDgACA/8/8A/wA//wD/AP8A/wD/
AP8A/wD/AOAAIH/z/wD/AH//AP8A/wD/AP8A/wD/AP8AwBggP/P/AP4//wD/AP8A/wD/AP8A
/wD/AIP/AOAAC/8A+n//AP8A/wD/AP8A/wD/AP8Ah/8A39/7/wDw/wD/AP8A/wD/AP8A/wD/
AP8AB/8A4/3z/wD4/wD/AP8A/wD/AP8A/wD/AP8Ah/8A+Ypz/wCB/wD/AP8A/wD/AP8A/wD/
AP8Af/8A3/lz+oP/AP8A/wD/AP8A/wD/AP8A/wA//wD8orNIA/8A/wD/AP8A/wD/AP8A/wD+
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
</binary>
 <binary id="img_8.png" content-type="image/png">iVBORw0KGgoAAAANSUhEUgAAAKgAAAApCAIAAAAuxfWeAAAE8UlEQVR4nO1aMUgzMRSO8G8O
WoSKCLqI4iTioCA46CZ0FOreQhdBqJMoInbsXhE3F1cVRws6CiIFQRAnoQoqaBEHJ/9HA+FM
mnfvLulde3ffUHrXd7nXfMl7L1/y7/f3lyWIH/6F7UDc8fr6+vLyMjU15WpZq9WGhobS6bSV
9ybEh4xqtZrNZimWMDiOj4+Jxq5IiA8UPT098CnSK0z33t5e+uNg3Gg0+vr6zD2JI/HQ+6FU
Nvy9nHuOu7u7kZERegtgfH9/Pzc3Z+5MvIh3dnpYcI6529vblZUV+rOpVOr6+joh3jN4p4dC
f8sw8/j46HXGPz09WfEnXsSHAjHOVO739/crlYrOnkN6pFQq7e3tmXuVEO8HUJTt7OwAbaen
p5lMBjcWYYZSWDjN2lqLJMT7QS6XOzw8hC+QcV2JZ14oDIZ1lhDvA7VaDZbU6XRajdK2EMC6
wz/xYUlOoQMWVP39/Vaa2traUm/irBcKBSuv9k+8JDkBu9vb22dnZ6rTdiWn0FGv1y8vL4vF
ItEeIRIGEFTpzsIez/FgPDY25strGSTiJb2JtZKcgPVyuQzFTssWLEpOoWNjY8NWU/Pz8x8f
H4J43s+iqleHCxhPT0+7NqvypcKdeFVvYhrJaXBwUNeIRcnJEKJzO2FbcnJy8urqSqRLV5dg
xs/Ozro2q/KlghrqJZ9Uyeno6Ojm5mZgYAAIXl1dhezlXG5alJwMEQzfYmDhIwxC4Pf3N71Z
MA5od07ntyo5wX8A1uFLtgnJ3qLk1C2gxFvA4uLiw8PD+Pi4a4NgBsbEV7u+V0s8ojcxjeQE
aV6X4xlBcoKRMTo6qvu1EyIzHXRv001QLF0HhzrUEBFQS7wnvYkDchUUfb5jEUSF7mK3o+BM
LtId1opHLNR7rYDW1tZOTk5g4sLkhsvPz88gy/g2bb14HYtB7gDpeOXfcRHQmnIH63j4zOfz
8OlvFyFKoT5I4PNT96tP4lXJCU/wjCA5GYb6DhkZ4bohxRtkTGDFHfIfVMlpeHi4Wq3qyk6L
klMCHURxJ6QXnzkegSQ5AXZ3dyHBLy0t8ctMJrO5uSkW7kTJqVtgSzB3BSKEO+Gk3Cm4ISKg
T+IlyYk1lyX5Jvhlo9GAqCDeR5ScrAD5t7YQWPTChXAOtaxjhP/+h3ii3sQIkhMQ75wWFiUn
HOYHGShPBRm9ECHcBPKMJ+pNTCM5wTqeO3pwcADBn9+kS052IeU5+lOuNjMzM36d8gZcCDfB
H+I99VFLyQnuqI1Q9EhboOxzsOYKQtyhbBtCyH1/f69UKlDAQuANTJ/AhXATxOgEDgSeiYkJ
6SYUoTCr+HekOIB6pVQq1ev19fV1ylkrAfzkJAWu62R/iDLxUpyHwAOXYs0Jv15cXDhzEHL4
utiEiQO+N4INhXAdIks8paN5FPX6FB061j1plG0SwqNJPJG/VCoVijN0jdJcCNchgsTjrEPY
NG+fkZcMhiGkTQmeRY94qaOlSyjgv76++PdCofD8/KwedCTueVB24cxzPC6EmyBqxDM9JTDX
c7lcuVzml8vLy+fn529vbwsLCyJretrposiohjIiLoSbIGrEI/0LhbEzbEInAuU/Pz+CdU/c
uBpbKRJxIdwEUSPeE7ruuLckhJsg1sSbIMgD2i2FcEMkxHuA87x6kAcuWgrhhkiI94YOOedj
joT4mOI/U201DSBMX6cAAAAASUVORK5CYII=</binary>
 <binary id="img_9.png" content-type="image/png">iVBORw0KGgoAAAANSUhEUgAAAGMAAAAoCAIAAABlxtNtAAADvElEQVR4nO1aP0g6URw/oUEQ
kwhsSMLJaKqxloLGQLcgBzeVGpxqaAkJGxp0SjCwzUHXEkcFXQonWwMjAh1sySLIyd8HD477
ne/ee+edepd+hrh7d++9r5/7/vm891ro9/vCHBxY4Hyv2+2GQqHNzc1YLOZ2u8dqkznBYOr5
+fni4mJvb8/n893d3b2+vt7f30cikckYZyowmAJNIAgXuVzO7/fDm3Z2dv4MUzabjT/5cEXf
7+/v4uIiLjqdDvga3TTTABxp7cJgKpFIhMPh1dXV6+vrVCpVrVbRMqp5JoLoSpr4YjCFFP7w
8CBenw4wsnFWB2/tm4PA1MnJye3tLU/ncrm8v7/PP5no7RZVcASmLi8vW61WsVhsNBqIPmI3
pPabm5tms8nPlFRoNFUc84DAFKRAMpkEU6JEIOpMNJ6dnZ2fn/PPZEV25CDnKejMx8dHSCcU
Pigpl8s1/A4aM5nMmM0zEVQz+vb2dj6fDwaD8C8DlYFFQ0+g176joyMkLETZ2tqaIbpcpMkk
ZInlhd8YhkqAgPr8/IxGo06nE8QZYpmeQQyE1q/F1lOxWAzrZIThxsaGWinkgVF+9P7+Dh83
ZChNYDOFMpdOp1EKt7a2eH6t5DVioAlGVz1UkkKhoNPBRwCXRsc3RHaHT/G8LImmMeUjMOX1
evHlJrxW52IK0edwOLSGnh6astns19eX2lOkzqurK/wlyhcJ+nOi/CewmYIcr9frE96Tok8n
rh/oNBkONlNQnlq3ENTUgCHxiIz+8/PD4+DGxj6DqVQqFQqFNI1IocMQ0+12++TTuUBnCiXm
8PCQfr4AKuUep0gN8lWxYBBT0zrvUGXq6ekJxY6uXJAvFC1yLhTXU9SccuEy8iBkpl5eXt7e
3ihO3u12a7VaIBAol8sjzz0ZyLOBnkRJYArFDms9+At0ObO/x+PhmcYkaz09q04CU/F4fDis
1LCyssL55rTIYk6KYioM1LXUgogZliAEpjID6LbwP5jBoYaBJLO+vq5ohPSHMBp+ebZOHBR+
7fP5cFupVMQ9bjylnAzMEFM84b+8vKz2iMwUfVArHrFwZsmlpSW1R2Sm6DRZ7oiFbipqPc8g
Sqbk/qLQislk0opnyAqaFLcoc9/f3+L18fFxu91WE9tKpuRi2iouw4Ta8gDeFA6H4QHi7cHB
QalU+vj42N3d5VIJahNY1Kco3xtLSOm/LoSBPgBHvV6PuJ9DiD5p6D/jU/yg7HkRfIqe/8a0
O25+EPIUs8+scSRihpSnTsyZ4sU/NCvOYvRcz14AAAAASUVORK5CYII=</binary>
 <binary id="img_10.png" content-type="image/png">iVBORw0KGgoAAAANSUhEUgAAACkAAAAlCAIAAABQwFfaAAACPElEQVR4nM2XMczBQBiGNfk3
wcZAjBITGyaJnU3CYENiMP0Gi4hYDEwkNdgMzMwGFmIiNgmxWExsNv+bNpGmvV4Pp/3f6Vzv
+tx9fe/7zs/j8bDpa7vd1mq1eDweCAQikcjxeNztdoVCgTKFXT/0xwAPBgM0hsNhMpl0u92x
WMwktqz7/e50OtG4XC5YARewMbvZbObzea/X22q1Op3OfD5Hj0nsUCg0mUzk9q8kXmBj9ldF
YJdKpX6/zzJ5NpslEgme7EajcT6fp9PpZrNBzInTYLput3s4HDizcZDa7TbY8gHDT+KYSqVS
rVbfBpPZEDLJcrnEUYbJcbJdLpd2DDpFUeTPhqLR6Gg0ymaziAHHc8XEhjKZDD48Yuv3+3nl
Mla2TTrT1+u1WCw6HA4sxVQ2VC6XUVEQ/GAwqGf7b7Fh6V6vB9uHw2F60ZMlCILcwGC5rTeL
Ka/he8N32DfLYJkkSKKvlYmNmNvt9lcDbhgkYzZS2Hq9tsDnNunW8Gr5kqOtjbmqx4CNmp3L
5d4AEx+p+mns8XicTqeJ+Vy5OGVUniZXrYNoeF32arWCseFwChgHT9WjfLuqrVqWLnu/359O
J0oiu91ui8UilUqhhFMWRxeBDWMjh2NPyGWG830+HwuGaAICu16va4OpJ4/HwzhSiyewRUmM
b2QU0fn/7K5oMZteBujV6VM2HfxMFx/i1WzlnlTZABe37/4vUSagz6P6Glsps/et/IoW7Jtu
IsNb2PtsljfyioeVueUPhpkds8GSV6gAAAAASUVORK5CYII=</binary>
 <binary id="img_11.png" content-type="image/png">iVBORw0KGgoAAAANSUhEUgAAALMAAAAmCAIAAAAA3T1UAAAExUlEQVR4nO1ZTSh1QRg+ys7C
/y2SLERK3aRQSrnZUNeKsrGjLBULioVY2rOwsyBLYkdRShak1I1kofxE+UmUFU93+qYxM2fu
nHPmnnudb56F3HPemfd9Z54z88w7hd/f346FhYDCXAdgkaewzLCQwzIjaigoKDCiECwzogNw
wmBvlhnRAVkqTPHDMsNCDssMCznCZsbW1lYymQzZqYUP5GbNMKuVRNjyXXBEeTd5fHy8v7+P
x+Nmuz07O6uqqorFYs4/ioOI9J+sus4G2HRYhMqMm5ubxsZGJ6xvem9vb2hoyFOTt7e34uJi
tQ3me319nfRMOEFKCOxC6MN1rsCmw8IDMyARHh4eRkdHfQdxdXXV2trKPqGjaZwr+GqLioq8
tpqamlpaWspohp5ZDnHBK1ybKkOZBZcOgQdm9Pf3428QZmDIWPfsMBkfsvPz89raWk9NLi8v
KyoqdCzRcyqV6ujooGGz8Utda0orDJG4sOuDePE6mDQd9mG+6AyyGhskx+np6eDgoKcmJycn
3d3dOpalpaXHx8fcUNL4pa41y1Crq6sTExMegpZ58QppOh6YMTY25sMrBURGXV0d+yRjGmji
pBlNn+joAALsXF7XjP39/bm5OR1L9ExioymwufhwnVvQdFjwzGA1Nqe36+vrWUto2tnZWYiP
tbW13t5e6YRhfW5oaCD/Y7yam5s1Y0VDolVZJJNJfFKa0S4vL3OKQWHspJfx29tbcSV3U0IL
Cwvz8/PS4EXX+Q8xHZ4ZVGOLepvOMQFosbKygqEERYaHh8fHxxOJBMZ3ZGRkc3MTBkdHR4eH
h3hFhhtrLAzcIuO2EvjCTyh80gRvd3d3xeaKaEWoja+vr7kSnMik/wry3US65VdWVn5+ftKf
ZPqd9LEH/2MWiUSl1MO+VVZWpnN+01EY5eXlQZpnNAaJe3p6uIchnCPwLbELIXY09i3RHFni
pTq7DLsJ27i6ulrRUSIN7iE+fZI28m9paZE21JxXqCRpW7dovRpPTk6+vr6KUYn7jllgQeUk
ZxAFahCSNcNtFPypqra2NoiG5+fnmpoa8a16OsGnjP27NZ+ZmdE3RoSw56SSmhMKPS51HQQh
LF1iOr+YIRYY2CcQFg6zWWiiq6trY2PDkRVCOFpwP3EMeX9/p3Hf3d1x1FRHW1JSAr1Nm6iN
Ly4uwGAuPEWtBT1zepwF5zr/IU1HdWo1UmDAhwjHGCw3F9LnRMkuLi6Sn319fdvb209PT+CZ
26mVi7azs/Pl5cVtejjjnZ2d6elp1oCVqOIgoGe3zVHh2l8ZKgRI0/nFDDZiMXqvqwXFwMBA
KpUSnysGCLsvVbhO+rwKTnx9fbG0UEfb1NR0cHBA77TUxtJDpiI8cL29vd3tLedap0MKHZEh
civgJYM0nTBqoPE0AnaiWeBi7T8+PgI6dQN6VtSws+dausQGv2SQppMv1fFsAGclttRmCuhT
UZjJquuMxRUflwxu6USZGbE0jHerM99Zci1FQNXilk6UmWHhZLoTUMAyI1IQtxJPFwgsLDOi
A4XC8KFMLTMiAre593SBwMIyIwpQT7k/iWqZ8eehuGRQ3wmoYZnxxyAtsWvqSrubRBni1Gru
I1aBWpiBZYaFHJYZFnL8AAig9q8ujGZjAAAAAElFTkSuQmCC</binary>
</FictionBook>
