ぼくのDOSS奮闘記 〜LibreOfficeを手探ってみた〜 3/3

論理和論理積、そしてシェル関数

発表まで残された日数はあと授業2回分しかないが、流石に実装したものが1つだけというのは物悲しいので、追加で機能を実装することにした。まず自分たちで思いついた案としては「if文で用いるような論理和論理積を記号を用いて"&&", "||"のように書けるようにする」というものがあった。もう一つ、先生が出してくれた案として、「libreoffice上からターミナル上で入力できるようなコマンドを実行できるようにする(シェル関数の実装)」というものがあり、これは自分たちには思いつかなかった面白い発想かつ大して面倒な実装ではない気がしたので、圧倒的感謝をしつつ早速取り組むことにした。

しかし、ここで1つ目の実装に6日もかかってしまったことを思い出した。また軽い気持ちで取り組んだらこの前のようになるのではないかという不安が3人の頭をよぎった。

だが、この2つの実装、どちらもあっけなく1日で終わってしまった。やはり前回のCombinationでノウハウを習得していたことが大きかっただろう。

簡単に実装内容を説明しておこう。まずシェル関数の実装に関しては=SHELL("")という形でコマンドを入力する形をとり、ユーザーが入力した文字列を中でpopen関数に渡すという形をとった。出力結果を返り値として取得してセルに反映させる。ただ1つ注意点として、libreofficeの中で文字列はOUStringという独自に定義した型を用いている。したがって入力として受け取ったOUString型をString型に一度戻してからpopenに流し込み、得られた出力結果は再度OUString型に直してからセルに出力するようにした。こうすることで各種シェルコマンドを実行してくれる関数が実装できたわけだが、当然セキュリティ的にはとても問題がありそうな匂いがするので実際に有用かと言われると微妙なところである。

さて、もう一つの実装である論理和論理積についてもまとめておく。こちらは一見すると組み合わせ演算子の時と変更点は全く同じように見えるが、一つだけつまずいたポイントがあった。それは演算子が&&など2文字で表されることである。どういうことかというと、組み合わせ演算子の時と同じようにするとプログラムは1文字目の&を読んだ時点でそれを演算子(&1文字だと文字列結合の演算子)と認識してしまい、演算子のあとに再び演算子→構文エラーとなってしまうのである。そこで、文字列から演算子を切り出す条件を決めている部分を探す必要があり、ここを見つけ出すのに少し苦労した。結果的にsc/source/core/tool/compiler.cxxに「この記号がきたら次来るのはこの要素」と決定している部分があった。言葉ではわかりづらいので具体的にコードを載せておく。

$ /* & */ t[38] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;

 38は&の文字コードで、この行は&演算子の後にCharまたはWordSepまたはValueSepが来ることを示していて、演算子としてはここで区切られることを示している。ここを変えてしまうことも考えたが、とりあえず&& と ||の時のみ対応させようと考え、「文字&または|を読み取った時、次の文字を見てそれが&か|だったらそれも同一の演算子に含める」という記述を加えることでこの問題は解決した。

おわりに

こうして追加で考えた2つの機能の実装も完了し、なんとか発表までにまとめることができた。一人では到底ここまで到達することができなかったので、ひとえに他のメンバーや教授、TAの方々のおかげである。ここに書いたことは実際に進捗を生んだ部分をかいつまんで書いているだけであり、本当はかなり何もできない時間が長かったことを知っておいてほしい。

この実験を通して普段いじらないような大規模なソフトに取り組めたことは貴重な経験だったし、デバッガがとても有効な手段だということを改めて知ることができた。また、全体像がわからなくても適当に存在しそうな変数名でgrepを実行すると案外たどり着くものだなという地道な努力の素晴らしさにも気づくことができた。後からこの記事を読んだ人がこの授業がどのようなものなのか、どのような成果が得られるのかを少しでも吸収してもらえたら嬉しい。