Estou tentando escrever um shell estritamente compatível com POSIX, mas o padrão não deixa claro como ir de bytes para caracteres. Ele diz para usar LC_CTYPE
, que vincula ainda mais ao conceito de um arquivo charmap , mas em nenhum lugar é declarado onde esses arquivos charmap estão localizados e como lê-los. man 5 charmap
diz que eles geralmente estão localizados em /usr/share/i18n/charmaps
, mas isso não é o suficiente para mim. Além disso, no meu sistema Linux, os arquivos naquele diretório são codificados em gzip, o que eu acho que pode não ser compatível, mas não encontrei nada sobre isso em lugar nenhum.
Estou pensando que deve haver alguns utilitários C padrão para obter os detalhes da codificação de caracteres atual (senão como alguém poderia usá-la?), mas não consegui encontrar nada do tipo na minha pesquisa na web. Existem as funções setlocale
and nl_langinfo
, a última das quais pode dar a você o nome da codificação de caracteres usada atualmente, mas isso não me ajuda a decodificá-la.
Ou eu deveria saber e implementar todas elas com antecedência?
Depois de fazer isso
setlocale()
,MB_CUR_MAX
você saberá se tem um conjunto de caracteres multibyte ou não.Se for multibyte, você pode usar as
mb*()
funções POSIX parambstowcs()
converter sequências de caracteres multibyte em caracteres largos.Então use
isblank()
/isalpha()
.. vsiswblank()
/iswalpha()
por exemplo se você precisa saber se um caractere é um espaço em branco/alfa... Vejaiconv()
para converter entre conjuntos de caracteres. Todas aswc*()
funções para trabalhar com caracteres largos (cuidado, nem todos os sistemas usam pontos de código Unicode para os valores wchar_t).Os caracteres usados na sintaxe do próprio shell devem fazer parte do conjunto de caracteres portátil (ASCII na prática atualmente), portanto, invariáveis entre localidades na prática¹.
Muito do que o shell faz é no nível de byte, então você pode escapar em muitos casos sem ter que decodificar. Há também a questão do que fazer com strings que não podem ser decodificadas em caracteres. Argumentos de linha de comando não precisam ser texto ou podem ser texto em uma variedade de conjuntos de caracteres diferentes, então os shells devem ser capazes de lidar com isso. Eles também devem ser capazes de lidar com a mudança de localidade no meio do caminho, como depois de
export LC_CTYPE=something-else
.Para um shell escrito hoje, acho que eu só suportaria UTF-8 como uma codificação de caracteres multibyte como
mksh
faz (com-o utf8-mode
), muitas das outras codificações de caracteres multibyte são perigosas, especialmente em um shell, então é melhor evitá-las de qualquer maneira. Então você pode ficar sem toda essamb*
API e fazer a decodificação/codificação UTF-8 manualmente.Você provavelmente vai querer verificar como outros shells fazem, aqui não é o lugar para fornecer um guia completo. Você verá que muitas vezes não é bonito. Por exemplo, a correspondência de padrões do bash com relação ao tratamento de multibytes é bem horrível e não é algo que eu recomendaria emular. Decodificar bytes que não podem ser decodificados em caracteres em valores especiais wchar_t como zsh/python fazem é provavelmente a melhor abordagem para lidar com não texto quando você tem que fazer uma operação de texto em strings (como correspondência de padrões, corte, comprimento de string...).
Shells que têm algum nível de suporte multibyte que você pode querer considerar incluem bash, zsh, ksh93, mksh, bosh e yash. O yash engasga com não texto.
¹ Embora o POSIX sugira, por exemplo, que qualquer caractere considerado em branco no locale pode ser usado como delimitador na sintaxe ou nomes de variáveis podem ter qualquer alnum, eu sugiro fortemente não ir por aí. Poucos shells fazem isso ou apenas parcialmente, e isso só torna a vida mais difícil para você e dá aos usuários uma API que não pode ser confiável. Observe que alguns sistemas ainda têm locales que têm ms-kangi como conjunto de caracteres. Esse conjunto de caracteres não tem um caractere de barra invertida e o byte 0x5c tem o símbolo iene (¥). Você geralmente consegue fazer isso porque as ferramentas, incluindo shells, não decodificam bytes em caracteres, especialmente em locais de byte único, então o ¥ geralmente pode ser usado no lugar de
\
there, pois as ferramentas apenas anexam o significado da barra invertida ao byte 0x5c.