XSLTを使ってXMLからHTMLを生成する(2020年版)

大学の課題でタイトルのようなことをやったんですが、ちょっと罠があったのでメモを残しておきます。
2020年5月26日執筆時点ではこの記事の通りにやれば動くと思いますが、将来的にどうなるかわかりません。

最近のブラウザではセキュリティ上の理由で、デフォルトのままではXSLTは動作しないようになっています。
ブラウザからローカルのファイルが自由に読み込めてしまってはダメなので当然といえば当然ですね。

Chromeでは動作しませんが単にFirefoxを使えば大丈夫です」のような記事が見つかるかもしれませんがそれも昔の話で、ブラウザの設定を変更する必要があります。


まずはXMLを準備します。

food.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="report.xsl"?>
<food>
  <vegetable>
    <name>きゅうり</name>
    <taste>おいしい</taste>
  </vegetable>
  <vegetable>
    <name>ピーマン</name>
    <taste>苦くておいしい</taste>
  </vegetable>
  <vegetable>
    <name>アボカド</name>
    <taste>とてもおいしい</taste>
  </vegetable>
  <fruit>
    <name>バナナ</name>
    <taste>おいしい</taste>
  </fruit>
  <fruit>
    <name>メロン</name>
    <taste>おいしい</taste>
  </fruit>
</food>
<?xml-stylesheet type="text/xsl" href="report.xsl"?>

この行がポイントです。href属性にxslファイルを指定しましょう。

続いてxslファイルを用意します。

report.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <html>
      <body>
        <h1>食べ物データ(主観)</h1>

        <h2>野菜</h2>

        <ul>
          <xsl:for-each select="/food/vegetable">
            <li>
              <xsl:value-of select="name" /><xsl:value-of select="taste" />
            </li>
          </xsl:for-each>
        </ul>

        <h2>果物</h2>

        <ul>
          <xsl:for-each select="/food/fruit">
            <li>
              <xsl:value-of select="name" /><xsl:value-of select="taste" />
            </li>
          </xsl:for-each>
        </ul>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

これで food.xmlFirefoxで開けばXMLから生成されたHTMLが表示される...という情報がわんさか出てくるんですが、やってみても表示されるのは無情にもXMLファイルのテキストだけ。

f:id:Kenta-s:20200526000225p:plain


というわけでFirefoxの設定を変更します(危険な変更なので終わったら必ず元に戻してください)。

新しいタブを開いて、about:config と打ち込みます。

f:id:Kenta-s:20200526000419p:plain

Accept the Risk and Continue をクリックして進みます。

検索バーで "file_unique" と入力すると

privacy.file_unique_origin が見つかるはずです。

f:id:Kenta-s:20200526000513p:plain

これはローカルのファイルをクロスオリジン扱いするか否か、といった設定で通常はtrueになっています。

これをfalseに変更します。

クロスオリジンとはなんぞ?というかたは、若干技術者向けではありますがこちらを軽く読んだ上で「異なるオリジンへのアクセスのことである」と思ってもらえれば大丈夫です。 developer.mozilla.org

この状態で food.xmlFirefoxのウィンドウにドロップすると以下のように report.xsl を元にしたHTMLが表示されるはずです。

f:id:Kenta-s:20200526000614p:plain

終わったら privacy.file_unique_origin をtrueに戻しておきましょう。

以上です。

大学(通信制)に入学して1ヶ月が経ちました

帝京大学理工学部情報科学科通信教育課程)に入学したのが2020年4月だったので、いつの間にか1ヶ月が経過していたことになります。
せっかくなので感じたことなど書いておこうと思います。

ちなみに僕は普段プログラマーとして働いているおじさん社会人です。


入学式が新型コロナウイルスの影響で中止になるという幕開けでした(もともと参加するつもりはなかったのでこれに関してはノーダメージだったわけですが)。
本来スクーリングが必須だった科目が動画で受講できるようになる等、大学関連へのコロナの影響はいまのところ自分にとっては都合が良い方向に動いています。

入学前には「難しすぎてついていけなかったらどうしよう」と心配していたんですが、
いくつか課題に取り組んでみた感触としては杞憂だったのかもしれません。

これまで全くと言っていいほど勉強してこなかった物理学に特に不安を感じていましたが、
必要とされる前提知識は三角関数などの簡単な数学くらいで、「テキストを理解するための知識がなくて詰み」のような事態に陥ることは(いまのところ)ありませんでした。
開講科目を眺める限り、2年目から地獄になりそうだなという予感があります。

あと「勉強のための時間が十分に作れるだろうか」といった不安もありましたが、
よくよく考えたらこれまでも単位のためでもないのにコンピュータやアルゴリズムを勉強したり(プログラマーならきっと誰でもやってますよね)、
微積分、線形代数なども雑に勉強したりしていたので生活に大きな変化は感じていません。

この記事を書いている今日はGW最終日で、このGWはほとんど課題をやっていましたが概ね楽しんで取り組むことができました。

プライベートでコードを書く量は圧倒的に減ってしまいましたが、それはまあ致し方ないですね。

まだ始まったばかりで見えてない部分もありそうなので、そのうちまた何か書こうと思います。


1年目が終わった感想

kenta-s.hatenadiary.jp

アクセスポイントを立てて、接続したスマホの通信をWiresharkでキャプチャする

Arch Linuxでアクセスポイントを立てて、そこに接続したスマホの通信をキャプチャします。

思い出しながら書いているのでもしかしたら何か抜けている手順があるかもしれません。

アクセスポイントについてはここを見れば基本的にはどうにかなります。

ソフトウェアアクセスポイント - ArchWiki

アクセスポイントを立てる

そもそもデバイスが対応していないと試合終了なのでまずは確かめます

$ iw list で、Supported interface modesセクションに「AP」があればOKです

Supported interface modes:
                 * IBSS
                 * managed
                 * AP
                 * AP/VLAN
                 * monitor
                 * P2P-client
                 * P2P-GO
                 * P2P-device

ありました。

続いてcreate_apをインストールします

sudo pacman -S create_ap

使い方はこちら

https://bbs.archlinux.org/viewtopic.php?pid=1269258

今回はパケットの中身を見たいので、Internet sharing from the same WiFi interfaceを使います

Internet sharing from the same WiFi interface:

./create_ap wlan0 wlan0 MyAccessPoint MyPassPhrase

僕の場合interfaceはwlp2s0なので、

$ sudo create_ap wlp2s0 wlp2s0 hoge hogehoge1

とやるとアクセスポイントhogeが立ち上がるのでスマホで接続します

f:id:Kenta-s:20200315215202j:plain

できました。

ちなみにこのスマホは昔使っていたGalaxy S9です。購入から一年も経たないうちにカメラがおかしくなって買い替える羽目になってしまい大変悲しかったです。

パケットを見る

wiresharkを立ち上げます

$ sudo wireshark

起動したら wlp2s0 を選んでキャプチャを開始します

f:id:Kenta-s:20200315205124p:plain

スマホのブラウザでwww.example.comにアクセスします

f:id:Kenta-s:20200315215228j:plain

www.example.comIPアドレスを問い合わせるためのDNSパケットが確認できました。無事キャプチャできているようです。

3つ下のDNSパケットで、www.example.comIPアドレスは93.184.216.34であることがわかります。

f:id:Kenta-s:20200315213521p:plain

ついでにSYN - SYN ACKのTCPストリーム

f:id:Kenta-s:20200315213544p:plain

HTTPストリーム

f:id:Kenta-s:20200315220159p:plain

以上です

仕事をしながらCSの学位を取得したい その2

この記事の続きです

kenta-s.hatenadiary.jp

2020年の4月から帝京大学理工学部情報科学科通信教育課程で学ぶことが決まりました。

背景とかいろいろ下書きに書いていたんですが、なんか生々しすぎたので全部消して報告だけ書くことにしましたw 直接会ったときにでも聞いてやってください。

がんばりまっす :v:

.gitignore_globalにメモ用のファイルを追加しておくと便利

$ cat ~/.gitignore_global 
kentas*

どのプロジェクトでも名前が被らなさそうな名前のファイルを適当に .gitignore_global しておくと、ところ構わずメモ帳の代わりにできて便利です

それだけです

LINUXプログラミングインタフェース 4章 ファイルI/O : 統一されたインタフェース

この記事内では便宜上カーネルシステムコールを呼び出す関数のこともシステムコールと書いています

open()システムコール

ファイルのopenには open() システムコールを使います

以下のようなファイルを用意してみました。

#include <stdio.h>                                                                                                                                                                                                                                                             
#include <sys/stat.h>                                                                                                                                                                                                                                                          
#include <fcntl.h>                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                               
int main(){                                                                                                                                                                                                                                                                    
  int fd;                                                                                                                                                                                                                                                                      
  fd = open("./test.txt", O_RDWR);  // O_RDWR は読み取りと書き込みモード                                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                                               
  if (fd == -1){                                                                                                                                                                                                                                                               
    printf("error!\n");                                                                                                                                                                                                                                                        
  }                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
  printf("fd is %d\n", fd);                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                               
  return 0;                                                                                                                                                                                                                                                                    
} 

open() はファイルディスクリプタを返します。ファイルディスクリプタはあまり大きくない整数で、 -1 はエラーを表します。

まずはopenするファイルがない状態で実行します

$ cc opentest.c
$ ./a.out 
error!
fd is -1

想定通り、open()は-1を返していました。

次はopen()するファイルを用意して実行します

$ touch test.txt
$ ./a.out 
fd is 3

ファイルディスクリプタは 3 だったようです。

open() すると、ファイルディスクリプタは常に利用できる最小の値が割り当てられます。

何度実行しても 3 が返ります。

$ ./a.out 
fd is 3
$ ./a.out 
fd is 3
$ ./a.out 
fd is 3

read()システムコール

ssize_t read(int fd, void *buffer, size_t count)

read()は、引数にファイルディスクリプタ、読み取ったデータを格納するメモリバッファアドレス、読み取るバイト数 を指定します。 返り値は、成功時には読み取ったバイト数、エラー時には-1です。

※ちなみに、読み取るバイト数より実際のファイルのバイト数が小さい場合は、読み取れるだけ読み取ります。

open()で返ってきたfdをread()してみます

#include <stdio.h>                                                                                                                                                                                                                                                             
#include <sys/stat.h>                                                                                                                                                                                                                                                          
#include <fcntl.h>                                                                                                                                                                                                                                                             
#include <unistd.h>                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
int main(){                                                                                                                                                                                                                                                                    
  int fd;                                                                                                                                                                                                                                                                      
  fd = open("./test.txt", O_RDWR);                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                               
  if (fd == -1){                                                                                                                                                                                                                                                               
    printf("error!\n");                                                                                                                                                                                                                                                        
  }                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
  printf("fd is %d\n", fd);                                                                                                                                                                                                                                                    
  
  // ここまではopen()と同じ
                                                                                                                                                                                                                                                                             
  char buffer[100];                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
  ssize_t numRead = read(fd, buffer, 100);                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                               
  printf("numRead is %d\n", numRead);
  printf(buffer);                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                               
  return 0;                                                                                                                                                                                                                                                                    
} 

まずはファイルをtouchしたばかりの状態で試します

$ cc readtest.c
$ ./a.out 
fd is 3
numRead is 0

想定どおり、fd: 3のファイルから読み取ったバイト数は0ですね

次はなにか適当に文字列を入れた状態で試してみます

$ echo "is this a pen?" > test.txt 
$ ./a.out 
fd is 3
numRead is 15
is this a pen?

read()は読み取ったバイト数である、15を返してきました。bufferにもデータが格納されていることがわかります。

write()システムコール

ssize_t write(int fd, void *buffer, size_t count);

read()に似てますね

#include <stdio.h>                                                                                                                                                                                                                                                             
#include <sys/stat.h>                                                                                                                                                                                                                                                          
#include <fcntl.h>                                                                                                                                                                                                                                                             
#include <unistd.h>                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
int main(){                                                                                                                                                                                                                                                                    
  int fd;                                                                                                                                                                                                                                                                      
  fd = open("./test.txt", O_RDWR);                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                               
  if (fd == -1){                                                                                                                                                                                                                                                               
    printf("error!\n");                                                                                                                                                                                                                                                        
  }                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
  printf("fd is %d\n", fd);                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                               
  char buffer[7] = "abcdefg";                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                               
  ssize_t numWrite = write(fd, buffer, 3);                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                               
  printf("numWrite is %d\n", numWrite);                                                                                                                                                                                                                                        
  printf(buffer);                                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                                               
  return 0;                                                                                                                                                                                                                                                                    
} 

bufferは"abcdefg"ですが、countが3バイトなので、abcだけがwriteされます。

$ cc writetest.c 
$ ./a.out 
fd is 3
numWrite is 3
abcdefg
$ cat test.txt 
abc

close()システムコール

int close(int fd)

返り値はcloseしたfdです

#include <stdio.h>                                                                                                                                                                                                                                                             
#include <sys/stat.h>                                                                                                                                                                                                                                                          
#include <fcntl.h>                                                                                                                                                                                                                                                             
#include <unistd.h>                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
int main(){                                                                                                                                                                                                                                                                    
  int fd;                                                                                                                                                                                                                                                                      
  fd = open("./test.txt", O_RDWR);                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                               
  if (fd == -1){                                                                                                                                                                                                                                                               
    printf("error!\n");                                                                                                                                                                                                                                                        
  }                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
  printf("fd is %d\n", fd);                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                               
  int closed = close(fd);                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                               
  printf("closed %d\n", fd);                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                               
  return 0;                                                                                                                                                                                                                                                                    
} 
$ cc closetest.c 
$ ./a.out 
fd is 3
closed 3

lseek()システムコール

off_t lseek(int fd, off_t offset, int whence)

offsetにはバイト数を指定、whenceにはSEEK_SET, SEEK_CUR, SEEK_ENDから指定します。

whenceがoffsetの原点になります。

まずはテスト用にファイルを準備します

$ echo "abcdefghij" > test.txt

以下は先頭をwhenceに指定して、offset 3でファイルを読み取る例です

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(){
  int fd;
  fd = open("./test.txt", O_RDWR);

  if (fd == -1){
    printf("error!\n");
  }

  printf("fd is %d\n", fd);

  lseek(fd, 3, SEEK_SET);

  char buffer[100];
  ssize_t numRead = read(fd, buffer, 100);

  printf("numRead is %d\n", numRead);
  printf(buffer);


  return 0;
}

d以降が読み取られていることがわかります

$ cc lseektest.c 
$ ./a.out 
fd is 3
numRead is 8
defghij

ファイルホール

ファイル末尾を超えた位置へシークしてread()すると、戻り値は0(end-of-file)になりますが、書き込みは可能です。
ファイル末尾からそれよりも後ろに新たに書き込まれた位置までをファイルホールといいます。

まずはテスト用にファイルを準備します

$ echo "abcdefghij" > test.txt

このファイルでは、whenceをSEEK_SET, offsetを11以上にするとファイル末尾を超えます。

試しにoffsetに20を設定してread()してみます

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(){
  int fd;
  fd = open("./test.txt", O_RDWR);

  if (fd == -1){
    printf("error!\n");
  }

  lseek(fd, 20, SEEK_SET);

  char buffer[100];
  ssize_t numRead = read(fd, buffer, 100);

  printf("numRead is %d\n", numRead);
  printf(buffer);


  return 0;
}

読み取ったバイト数は0になります

$ cc lseektest.c 
$ ./a.out 
numRead is 0

次は書き込みです

10バイトしか書き込まれていないファイルに対して、offset 20で"aaa"を書き込みます

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(){
  int fd;
  fd = open("./test.txt", O_RDWR);

  if (fd == -1){
    printf("error!\n");
  }

  lseek(fd, 20, SEEK_SET);

  char buffer[3] = "aaa";
  ssize_t numWritten = write(fd, buffer, 3);

  printf("numWritten is %d\n", numWritten);
  printf(buffer);


  return 0;
}

出力は通常の書き込みと同じです

$ cc lseektest.c 
$ ./a.out 
numWritten is 3
aaa

ファイルの中は以下のようになっています

$ cat test.txt 
abcdefghij
aaa

abcdefghij と aaaの間(ファイルホール)は改行コードがあるように見えますが実際にはNULLです。

ファイルシステムはファイルホールに対してはディスクブロックを割り当てず、あとでデータが書き込まれた時点で初めて割り当てます。少ないブロック数で済むのでファイルホールはディスク領域の節約にも繋がります。

コアダンプファイルはファイルホールを持つ代表例です。

日本語キーボードを英語配列で使うとパイプができない問題をxmodmapで解決する

日本語キーボードを英語配列に設定して使うと、そのままの状態ではパイプが入力できません。

Xmodmapを使ってキーをマッピングすると良いです

xjman.dsl.gr.jp

まずはxevコマンドを使って、パイプを割り当てたいキーのコードを調べます

$ xev

なにかキーを打つとそのキーに関する情報が出力されます。

以下はaを入力した例です

KeyPress event, serial 40, synthetic NO, window 0x3c00001,
    root 0x1c1, subw 0x0, time 580142, (277,890), root:(277,919),
    state 0x0, keycode 38 (keysym 0x61, a), same_screen YES,
    XLookupString gives 1 bytes: (61) "a"
    XmbLookupString gives 1 bytes: (61) "a"
    XFilterEvent returns: False

3行目の keycode 38 の部分がキーコードなのでこれを覚えておきます

ホームディレクトリに.Xmodmapファイルを作ってさきほど調べたキーにパイプを割り当てます

$ vim .Xmodmap
// keycode {key} = {keyを押したとき} {key + shiftを押したとき}
keycode 132 = backslash bar

以下のコマンドで反映されます

$ xmodmap .Xmodmap