# interp

Команда создает и управляет Tcl-интерпретаторами.

## СИНТАКСИС

          interp option ?arg arg...?

## ОПИСАНИЕ

Эта команда позволяет создавать один или несколько новых Tcl-интерпретаторов, которые сосуществуют в одном приложении с создавшим их интерпретатором. Создавший интерпретатор называется мастер-интерпретатором, а созданные интерпретаторы называются подчиненными (slave) интерпретаторами. Мастер-интерпретатор может создавать произвольное число подчиненных интерпретаторов, а каждый из подчиненных может в свою очередь создавать подчиненные интерпретаторы, для которых он сам является мастер-интерпретатором. В результате в приложении может создаваться иерархия интерпретаторов.

Каждый интерпретатор независим от остальных. Он имеет собственное пространство имен для команд, процедур и глобальных переменных. Мастер-интерпретатор может создавать связи между подчиненными интерпретаторами и собой, используя механизм алиасов. Алиас - это команда в подчиненном интерпретаторе, которая, при ее вызове, вызывает другую команду в мастер-интерпретаторе или в другом подчиненном интерпретаторе. Кроме механизма алиасов, связь между интерпретаторами поддерживается только через переменные окружения. Массив env>обычно является общим для всех интерпретаторов в приложении. Необходимо заметить, что идентификаторы каналов (например, идентификатор, возвращаемый командой open) больше не разделяются между интерпретаторами, как это было в предыдущих версиях Tcl. Чтобы обеспечить совместный доступ к каналам, необходимо использовать явные команды для передачи идентификаторов каналов из интерпретатора в интерпретатор.

Команда interp позволяет также создавать надежные интерпретаторы. Надежный интерпретатор - это интерпретатор с существенно урезанной функциональностью, поэтому он может исполнять ненадежные скрипты без риска нарушить работу вызывающего их приложения. Например, из безопасных интерпретаторов недоступны команды создания каналов и подпроцессов. Более подробно см. Безопасные интерпретаторы. Опасная функциональность не удалена из безопасных интерпретаторов, но скрыта таким образом, что только надежные интерпретаторы могут получить к ней доступ. Более подробно см. Скрытые команды. Механизм алиасов может быть использован для безопасного взаимодействия между подчиненным интерпретатором и его мастер-интерпретатором. Более подробно этот вопрос обсуждается в разделе "Использование алиасов".

Полное имя интерпретатора представляет собой список, содержащий имена его предков в иерархии интерпретаторов и заканчивающийся именем интерпретатора в его непосредственном предке. Имена интерпретаторов в списке - это их относительные имена в их непосредственных мастер-интерпретаторах. Например, если a есть подчиненный интерпретатор текущего интерпретатора и, в свою очередь, имеет подчиненный интерпретатор a1, а тот, в свою очередь, имеет подчиненный интерпретатор a11, то полное имя a11 в a есть список {a1 a11}.

В качестве аргумента описанной ниже команды interp используется полное имя интерпретатора. Интерпретатор, в котором исполняется команда, всегда обозначается как {} (пустая строка). Обратите внимание, что в подчиненном интерпретаторе невозможно сослаться на мастер-интерпретатор кроме как через алиасы. Также нет никакого имени, под которому можно было бы сослаться на мастер-интерпретатор, первым созданный в приложении. Оба ограничения вызваны соображениями безопасности.

## КОМАНДА INTERP

Команда interp используется для создания, удаления и выполнения команд в подчиненном интерпретаторе, а также для разделения или передачи каналов между интерпретаторами. Она может иметь одну из перечисленных ниже форм в зависимости от значения аргумента option.

interp alias srcPath srcCmd
  Возвращает список, состоящий из исходной команды и аргументов, связанных с алиасом srcCmd в интерпретаторе srcPath (возвращаются значения, использовавшиеся при создании алиаса, так как имя команды могло быть изменено с помощью команды rename).
interp alias srcPath srcCmd {}
  Удаляет алиас srcCmd в подчиненном интерпретаторе srcPath. Имя srcCmd - это имя, под которым алиас был создан. Если созданная команда была переименована, то будет удалена переименованная команда.
interp alias srcPath srcCmd targetPath targetCmd ?arg arg...?

  Эта команда создает алиас между двумя подчиненными интерпретаторами (для создания алиаса между подчиненным интерпретатором и мастер-интерпретатором используется команда slave alias). Оба интерпретатора srcPath и targetPath должны быть в иерархии интерпретаторов ниже того интерпретатора, в котором выполняется команда. Аргументы srcPath и srcCmd задают интерпретатор, в котором будет создан алиас и его имя. Аргумент srcPath должен быть Tcl-списком, задающим имя существующего интерпретатора. Например, "a b" определяет интерпретатор b, который является подчиненным интерпретатором интерпретатора a, который в свою очередь является подчиненным интерпретатором текущего интерпретатора. Пустой список соответствует текущему интерпретатору (в котором исполняется команда). Аргумент srcCmd определяет имя новой команды-алиаса, которая будет создана в интерпретаторе srcPath. Аргументы targetPath и targetCmd определяют целевой интерпретатор и команду, а аргументы arg, если они есть, определяют дополнительные аргументы для команды targetCmd, которые будут вставлены перед аргументами, заданным при вызове srcCmd. Команда targetCmd может как существовать, так и не существовать в момент создания алиаса. В последнем случае она не создается командой interp alias.

  Алиас позволяет использовать команду targetCmd в интерпретаторе targetPath каждый раз, когда вызывается команда-алиасsrcCmd в интерпретаторе srcPath. Подробнее см. "Использование алиасов".

interp aliases ?path?
  Эта команда возвращает список имен всех команд-алиасов, определенных в интерпретаторе path.
interp create ?-safe? ?--? ?path?

  Создает подчиненный интерпретатор с именем path и новую команду для работы с этим интерпретатором, называемую также подчиненной (slave) командой. Имя подчиненной команды совпадает с последним элементом списка path. Новый подчиненный интерпретатор и подчиненная команда создаются в интерпретаторе, имя которого состоит из всех элементов списка path, кроме последнего. Например, если аргумент path равен a b c, то в результате в интерпретаторе a b будет создан подчиненный интерпретатор c и подчиненная команда c. Синтаксис подчиненной команды описан ниже см. "Команда работы с интерпретатором". Если аргумент path отсутствует, Tcl создает уникальное имя в форме interpx, где x - целое число, и использует его для подчиненного интерпретатора и подчиненной команды. Если в команде указана опция -safe или если мастер-интерпретатор сам является безопасным интерпретатором, новый подчиненный интерпретатор будет безопасным, то есть с ограниченной функциональностью. В противном случае новый интерпретатор будет включать полный набор встроенных Tcl-команд и переменных. Аргумент - используется для того, чтобы обозначить конец опций. Следующий аргумент будет использоваться как имя интерпретатора, даже если он равен -safe.

  Команда interp create возвращает имя нового интерпретатора. Имя подчиненного интерпретатора должно быть уникальным среди подчиненных интерпретаторов его мастера. Если у мастер-идентификатора уже существует.

interp delete ?path...?
  Удаляет ноль или больше интерпретаторов с именем path. Для каждого удаляемого интерпретатора удаляются также его подчиненные интерпретаторы. Если для одного из аргументов path интерпретатора с таким именем не существует, команда генерирует ошибку.
interp eval path arg ?arg...?
  Команда объединяет все аргументы так же, как команда concat, а затем исполняет сформированный скрипт в подчиненном интерпретаторе, заданном аргументом path. Результат выполнения (включая информацию об ошибках в переменных errorInfo и errorCode, если произошла ошибка) возвращается в вызывающий интерпретатор.
interp exists path
  Возвращает 1, если подчиненный интерпретатор с именем path существует в его мастер-интерпретаторе. В противном случае возвращает 0. Если аргумент path представляет относительное имя, то он ищется в том интерпретаторе, в котором выполняется команда.
interp expose path hiddenName ?exposedCmdName?
  Разрешает использование в интерпретаторе path скрытой команды hiddenName под новым именем exposedCmdName (в настоящее время поддерживаются только имена в глобальном пространстве имен, не содержащие "::"). Если обычная (не скрытая) команда exposedCmdName уже существует, генерируется сообщение об ошибке. Скрытые команды обсуждаются подробно см. "Скрытые команды".
interp hide path exposedCmdName ?hiddenCmdName?
  Запрещает использование в интерпретаторе path обычной команды exposedCmdName и переименовывает ее в скрытую команду hiddenCmdName (или в скрытую команду под старым именем, если новое не было задано). Если скрытая команда с заданным именем уже существует, команда возвращает ошибку. В настоящее время exposedCmdName и hiddenCmdName не могут содержать "::". Команды, которые должны быть скрыты с помощью interp hide, ищутся только в глобальном пространстве имен, даже если текущее пространство имен не глобальное. Скрытые команды обсуждаются подробно ниже (см. "Скрытые команды").
interp hidden path
  Возвращает список скрытых команд интерпретатора path.
interp invokehidden path ?-global? hiddenCmdName ?arg...?
  Вызывает в интерпретаторе path скрытую команду hiddenCmdName с перечисленными аргументами. Никаких подстановок или вычислений в аргументах не производится. Если указана опция -global, скрытая команда выполняется на глобальном уровне в целевом интерпретаторе. В противном случае она выполняется в текущем контексте и может использовать значения локальных переменных и переменных из вышестоящих стеков. Скрытые команды обсуждаются подробно в соответствующем разделе ниже.
interp issafe ?path?
  Возвращает 1, если интерпретатор path безопасный, и 0 в противном случае.
interp marktrusted path
  Отмечает интерпретатор path как надежный. Не раскрывает скрытые команды. Команда interp marktrusted может выполняться только из надежного интерпретатора. Если интерпретатор path уже надежный, команда не оказывает никакого воздействия.
interp share srcPath channelId destPath
  Позволяет разделить канал ввода - вывода channelId между интерпретаторами srcPath и destPath. Оба интерпретатора после этого будут иметь одинаковые права на канал. Каналы ввода - вывода, доступные в интерпретаторе, автоматически закрываются, когда удаляется интерпретатор.
interp slaves ?path?
  Возвращает список подчиненных интерпретаторов для интерпретатора path. Если аргумент path отсутствует, возвращает список подчиненных интерпретаторов для интерпретатора, в котором выполняется команда.
interp target path alias
  Возвращает список, описывающий целевой интерпретатор (интерпретатор, в котором выполняется реальная команда при вызове команды-алиаса) для алиаса alias. Алиас задается именем интерпретатора path и команды-алиаса alias как в команде interp alias выше. Имя целевого интерпретатора возвращается как имя интерпретатора относительно имени интерпретатора, в котором выполняется команда. Если это текущий интерпретатор, то возвращается пустой список. Если этот интерпретатор не является потомком интерпретатора, в котором выполняется команда, генерируется ошибка. Реальная команда не обязана быть определена в момент выполнения данной команды.
interp transfer srcPath channelId destPath
  Делает канал ввода - вывода channelId доступным в интерпретаторе destPath и недоступным в интерпретаторе srcPath.

## КОМАНДА РАБОТЫ С ИНТЕРПРЕТАТОРОМ (ПОДЧИНЕННАЯ КОМАНДА)

Для каждого подчиненного интерпретатора, созданного с помощью команды interp, в мастер-интерпретаторе создается команда с тем же именем, что и подчиненный интерпретатор. Эта команда предназначена для выполнения различных операций в подчиненном интерпретаторе. В общем случае она имеет следующий вид:

          slave command ?arg arg...?

где slave - имя подчиненного интерпретатора, а аргументы command и arg arg... определяют конкретное назначение команды. Ниже перечислены возможные формы команды.

slave aliases
  Возвращает список всех алиасов в подчиненном интерпретаторе slave. Возвращаемые имена - это имена, использовавшиеся при создании соответствующих алиасов. Они могут не совпадать с текущими именами команд, если те были переименованы.
slave alias srcCmd
  Возвращает список, состоящий из имени реальной команды и ее аргументов, ассоциированных с алиасом srcCmd. Возвращаются те же значения, что и в команде создания алиаса. Если созданный алиас был переименован, то в команде надо указывать имя srcCmd, которое использовалось при создании алиаса.
slave alias srcCmd {}
  Удаляет алиас srcCmd в подчиненном интерпретаторе. Аргумент srcCmd указывает имя алиаса в момент создания. Если после этого он был переименован, удалится переименованная команда.
slave alias srcCmd targetCmd ?arg..?
  Эта команда создает команду - алиас в подчиненном интерпретаторе. Каждый раз, когда в подчиненном интерпретаторе будет вызываться команда srcCmd, реально выполняться будет команда targetCmd в мастер-интерпретаторе. Аргументы arg, если они есть, определяют дополнительные аргументы для команды targetCmd, которые будут вставлены перед аргументами, заданными при вызове srcCmd. Подробнее см. "Использование алиасов".
slave eval arg ?arg..?
  Эта команда объединяет все свои аргументы так же, как команда concat, и выполняет сформированный таким образом скрипт в подчиненном интерпретаторе. Результат выполнения (включая информацию об ошибках в переменных errorInfo и errorCode, если произошла ошибка) возвращается в вызывающий интерпретатор.
slave expose hiddenName ?exposedCmdName?
  Разрешает использование в подчиненном интерпретаторе скрытой команды hiddenName под новым именем exposedCmdName (в настоящее время поддерживаются только имена в глобальном пространстве имен, не содержащие "::"). Если обычная (не скрытая) команда exposedCmdName уже существует, генерируется сообщение об ошибке. Скрытые команды обсуждаются ниже (см. "Скрытые команды").
slave hide exposedCmdName ?hiddenCmdName?
  Запрещает использование в подчиненном интерпретаторе обычной команды exposedCmdName и переименовывает ее в скрытую команду hiddenCmdName (или в скрытую команду под старым именем, если новое не было задано). Если скрытая команда с заданным именем уже существует, команда возвращает ошибку. В настоящее время exposedCmdName и hiddenCmdName не могут содержать "::". Команды, которые должны быть скрыты с помощью interp hide, ищутся только в глобальном пространстве имен, даже если текущее пространство имен не глобальное. Скрытые команды обсуждаются ниже (см. "Скрытые команды").
slave hidden
  Возвращает список скрытых команд в подчиненном интерпретаторе.
slave invokehidden ?-global? hiddenName ?arg..?
  Вызывает в подчиненном интерпретаторе скрытую команду hiddenCmdName с перечисленными аргументами. Никаких подстановок или вычислений в аргументах не производится. Если указана опция -global, скрытая команда выполняется на глобальном уровне в подчиненном интерпретаторе. В противном случае она выполняется в текущем контексте и может использовать значения локальных переменных и переменных из вышестоящих стеков. Скрытые команды обсуждаются подробно в соответствующем разделе ниже.
slave issafe
  Возвращает 1, если подчиненный интерпретатор безопасный, и 0 в противном случае.
slave marktrusted
  Отмечает подчиненный интерпретатор как надежный. Не раскрывает скрытые команды. Команда может выполняться только из надежного интерпретатора. Если подчиненный интерпретатор уже надежный, команда не оказывает никакого воздействия.

## БЕЗОПАСНЫЕ ИНТЕРПРЕТАТОРЫ

Безопасный интерпретатор - это интерпретатор с ограниченной функциональностью. Поэтому в нем можно без опасений выполнять произвольные скрипты, даже написанные вашим злейшим врагом, не опасаясь нарушить выполнение вашего приложения или испортить содержимое дисков вашего компьютера. Чтобы обеспечить безопасность, из таких интерпретаторов удалены определенные команды и переменные. Например, команда создания файлов на диске и команда запуска подпроцессов. Тем не менее, в безопасном интерпретаторе можно обеспечить ограниченный доступ к подобным командам с помощью механизма алиасов. В результате при выполнении потенциально опасных команд будут вызываться специально написанные процедуры в мастер-интерпретаторе, которые могут тщательно проверять заданные аргументы и позволять использовать только ограниченный набор средств. Например, создание файлов может быть разрешено только в ограниченном наборе подкаталогов, а запуск подпроцессов разрешен только для ограниченного (и фиксированного) набора хорошо проверенных программ.

Чтобы создаваемый интерпретатор был безопасным, необходимо при его создании указать опцию -safe. Кроме того, любой интерпретатор, созданный из безопасного интерпретатора, также будет безопасным.

Безопасный интерпретатор создается со следующим набором команд:

after | append | array | break | case
catch | clock | concat | continue | eof
error | eval | expr | fblocked | fileevent
flush | for | foreach | format | gets
global | history | if | incr | info
interp | join | lappend | lindex | linsert
list | llength | lower | lrange | lreplace
lsearch | lsort | package | pid | proc
puts | read | rename | return | scan
seek | set | split | string | subst
switch | tell | trace | unset | update
uplevel | upvar | vwait | while |

Следующие команды в безопасном интерпретаторе скрыты:

cd | exec | exit | fconfigure
file | glob | load | open
pwd | socket | source |

Эти команды могут быть созданы заново как Tcl-процедуры или алиасы или разрешены к использованию с помощью команды interp expose.

Сверх того, в безопасных интерпретаторах отсутствует массив env, содержащий обычно переменные окружения. Это связано с тем, что в переменных окружения может храниться конфиденциальная информация.

Расширения, загружаемые в безопасный интерпретатор, также обладают ограниченной функциональностью. Более подробно эти вопросы обсуждаются в пп. SafeTcl и load.

## ИСПОЛЬЗОВАНИЕ АЛИАСОВ

Механизм алиасов весьма тщательно спроектирован таким образом, чтобы он позволял без опасений выполнять ненадежные скрипты в безопасных подчиненных интерпретаторах, используя для алиасов проверенные команды в мастер-интерпретаторах. Для обеспечения безопасности принципиально важно, чтобы информация из подчиненного интерпретатора никогда не обрабатывалась (то есть скрипты не исполнялись, и в них не производились подстановки) в мастер-интерпретаторе. В противном случае всегда будет оставаться возможность написать такой скрипт для подчиненного интерпретатора, который выполнил бы произвольные действия в мастер-интерпретаторе и мог бы нарушить его безопасность.

Когда в подчиненном интерпретаторе вызывается команда-алиас, необходимые Tcl-подстановки в аргументах выполняются там же, как для обычной Tcl-команды. После этого аргументы объединяются с целевой командой и ее аргументами, определенными при создании алиаса. Так, если команда вызова алиаса имела вид srcCmd arg1 arg2... argN'', то сформируется команда targetCmd arg arg... arg arg1 arg2... argN'', где arg arg... arg - аргументы, указанные при создании алиаса. После этого сформированная команда выполняется в целевом интерпретаторе (со сформированным набором аргументов). Если в целевом интерпретаторе нет команды targetCmd, будет сгенерирована ошибка. Перед выполнением команды в целевом интерпретаторе больше никаких подстановок в аргументах произведено не будет. Команда будет вызвана напрямую, а не через обычный механизм выполнения. Таким образом, подстановки в каждом слове команды оказываются выполненными ровно один раз: в targetCmd и arg... arg - при выполнении команды, с помощью которой был создан алиас, а в arg1 - argN - при анализе команды алиаса в соответствующем интерпретаторе.

При написании процедуры targetCmd, которую предполагается использовать для алиаса в безопасном интерпретаторе, нужно придерживаться следующего правила. Недопустимо, чтобы в значениях ее аргументов в теле процедуры производились подстановки, либо чтобы аргументы были скриптами, исполняемыми в теле процедуры. Нарушение этого правила дает возможность подчиненному интерпретатору выполнить произвольный код в мастер-интерпретаторе и нарушить, таким образом, безопасность системы.

## СКРЫТЫЕ КОМАНДЫ

Безопасные интерпретаторы существенно ограничивают функциональные возможности выполняемых в них Tcl-скриптов. С одной стороны, это позволяет избежать нежелательных последствий при их исполнении, но, с другой стороны, рано или поздно возникает насущная необходимость выполнить потенциально опасную команду в безопасном интерпретаторе. Например, прочитать дополнительный скрипт с помощью команды source. Или выполнить в Tk-приложении некоторые команды управления окнами.

Команда interp обеспечивает решение этой проблемы с помощью механизма скрытых команд. Потенциально опасные команды не удаляются из безопасного интерпретатора, а становятся скрытыми. При этом их невозможно выполнить из скрипта, выполняемого "внутри" интерпретатора. Но их можно выполнить "извне" из любого надежного предка интерпретатора с помощью команды interp invoke. Скрытые и обычные (не скрытые) команды размещаются в различных пространствах имен. Это позволяет иметь в одном интерпретаторе скрытую и обычную команды с одним и тем же именем.

Скрытые команды могут использоваться в теле процедуры, использованной при определении алиаса. Например, в подчиненном интерпретаторе можно создать алиас source. Вызываемая при этом процедура в мастер-интерпретаторе проверяет допустимость запрошенной операции (например, что запрошенный файл находится в соответствующем каталоге, к которому разрешен доступ из подчиненного интерпретатора) и вызывает в подчиненном интерпретаторе скрытую команду source. Обратите внимание, что в этом случае в подчиненном интерпретаторе существует две команды source: скрытая и алиас.

Из мастер-интерпретатора возможен вызов скрытых команд подчиненного интерпретатора через механизм алиасов. Поэтому в теле процедур, предназначенных для создания алиаса, необходимо избегать подстановок и команд исполнения для аргументов. См. "Использование алиасов".

Безопасные интерпретаторы не могут "изнутри" вызвать скрытую команду в себе или своих потомках.

Множество скрытых команд может быть изменено из надежного интерпретатора с помощью команд interp expose и interp hide. Безопасный интерпретатор не может изменить набор скрытых команд в себе или своих потомках.

В настоящее время имена скрытых команд не могут содержать имени пространства имен. Поэтому, прежде чем скрыть команду, ее надо перевести в глобальное пространство имен. Команды, которые необходимо скрыть, должны находиться в глобальном пространстве имен. Это позволяет избежать ошибочного скрытия одноименной команды из другого пространства имен.