試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

システム上の全ディスク(disk)の各パーティションにあるファイルシステムのUUIDをGUI上で確認・クリップボードにコピーできるツールを作成

GUdevライブラリでGNU/Linuxシステム上のデバイス情報を取得する(ページ1/3)」とその続きの記事ではGUdevライブラリを用いてシステム上のデバイスの各種情報を表示するテストを行ったが、これを応用したものとして、システム上の全ディスク(disk)の各パーティションにあるファイルシステムのUUIDをGUI上でツリー構造で確認できるツールを作成した。
選択した項目のUUIDもメニュー項目からクリップボードに簡単にコピーできる。データはGTK+のTreeStoreに格納し、表示はそれに関連付けたツリービューで行っている。並ぶ順番はディスク内の位置(項目UDISKS_PARTITION_OFFSETの値)の並びと同じになるように工夫した。
コンパイルにはVala本体(Valaコンパイラ)の他、GTK+とGUdevの両方の開発パッケージが必要。

画像は仮想マシン上のもの(sdbのディスクはこの画像のために適当に構成/フォーマットしただけ)だが、「VBOX_HARDDISK」の部分はディスクのモデル名となる。
[任意]ファイル名: list-uuid-gtk.vala ライセンス: GPL-3 (or lator)

/*
 * 全ディスクの各パーティションにあるファイルシステムのUUIDをツリー表示するGUIツール
 * version 20101108
 * (C) 2010 kakurasan
 * Licensed under GPLv3+
 *
 * valac --pkg posix --pkg gudev-1.0 --pkg gtk+-2.0 list-uuid-gtk.vala -o list-uuid-gtk
 */

using Posix;
using GUdev;
using Gtk;
using Gdk;

namespace ListUuidGtk
{
  class UuidView : Gtk.TreeView
  {
    public enum Column
    {
      NAME,    // デバイス
      FSTYPE,  // ファイルシステム
      UUID,    // UUID
      OFFSET,  // オフセット
      NUMBER   // 項目数
    }
    Gtk.TreeViewColumn col_name;
    Gtk.TreeViewColumn col_fstype;
    Gtk.TreeViewColumn col_uuid;
    Gtk.ImageMenuItem item_copy;
    Gtk.Menu menu_popup;
    weak MainWindow win;  // weakは参照の循環防止
    public
    UuidView (MainWindow win, Gtk.TreeModel model)
    {
      GLib.Object (model: model);  // ツリーモデルの関連付け
      this.win = win;
      // コラムの作成
      this.col_name = new Gtk.TreeViewColumn.with_attributes ("Device/Name",
                                                              new Gtk.CellRendererText (),
                                                              "text", UuidView.Column.NAME);
      this.col_fstype = new Gtk.TreeViewColumn.with_attributes ("File system",
                                                                new Gtk.CellRendererText (),
                                                                "text", UuidView.Column.FSTYPE);
      this.col_uuid = new Gtk.TreeViewColumn.with_attributes ("UUID",
                                                              new Gtk.CellRendererText (),
                                                              "text", UuidView.Column.UUID);
#if DEBUG
      var col_offset = new Gtk.TreeViewColumn.with_attributes ("Offset",
                                                               new Gtk.CellRendererText (),
                                                               "text", UuidView.Column.OFFSET);
#endif
      // コラムヘッダの中央寄せ
      this.col_name.alignment = this.col_fstype.alignment = this.col_uuid.alignment = 0.5f;
      // コラムの追加
      this.append_column (this.col_name);
      this.append_column (this.col_fstype);
      this.append_column (this.col_uuid);
#if DEBUG
      this.append_column (col_offset);
#endif
      // コンテキストメニュー
      this.item_copy = new Gtk.ImageMenuItem.from_stock (Gtk.STOCK_COPY, null);
      this.menu_popup = new Gtk.Menu ();
      this.menu_popup.add (this.item_copy);
      this.menu_popup.show_all ();
      // メニューのポップアップ
      this.button_press_event.connect ((ev) =>
      {
        if (ev.button == 3)
        {
          // データがない部分でコンテキストメニューが開けないようにする
          // get_path_at_pos()はTreeView内の座標から項目のパスを取得
          bool path_at_pos = this.get_path_at_pos ((int) ev.x, (int) ev.y, null, null, null, null);
          // クリックした場所に項目があるのが確認できた場合のみポップアップする
          if (path_at_pos)
            this.menu_popup.popup (null, null, null, ev.button, ev.time);
        }
        return false;
      });
      // メニュー項目のハンドラ
      this.item_copy.activate.connect (this.win.copy_uuid);
      // シマシマを付ける
      this.rules_hint = true;
      // ツリーを展開する
      this.expand_all ();
    }
  }
  class MainWindow : Gtk.Window
  {
    Gtk.AccelGroup accelgroup;
    Gtk.ImageMenuItem item_quit;
    Gtk.Menu menu_file;
    Gtk.Menu menu_edit;
    Gtk.MenuItem item_file;
    Gtk.MenuItem item_edit;
    Gtk.MenuItem item_copy;
    Gtk.MenuBar menubar;
    Gtk.ScrolledWindow sw;
    Gtk.VBox vbox;
    Gtk.Clipboard cb;
    UuidView tv;
    public
    MainWindow (Gtk.TreeStore ts)
    {
      // クリップボード
      this.cb = Gtk.Clipboard.get (Gdk.SELECTION_CLIPBOARD);
      // ショートカットキー(アクセラレータ)
      this.accelgroup = new Gtk.AccelGroup ();
      this.add_accel_group (this.accelgroup);
      // メニュー
      this.item_quit = new Gtk.ImageMenuItem.from_stock (Gtk.STOCK_QUIT, accelgroup);
      this.item_copy = new Gtk.ImageMenuItem.from_stock (Gtk.STOCK_COPY, accelgroup);
      this.menu_file = new Gtk.Menu ();
      this.menu_file.add (item_quit);
      this.menu_edit = new Gtk.Menu ();
      this.menu_edit.add (item_copy);
      this.item_file = new Gtk.MenuItem.with_mnemonic ("_File");
      this.item_edit = new Gtk.MenuItem.with_mnemonic ("_Edit");
      this.item_file.set_submenu (menu_file);
      this.item_edit.set_submenu (menu_edit);
      this.menubar = new Gtk.MenuBar ();
      this.menubar.append (item_file);
      this.menubar.append (item_edit);
      // ツリービュー
      this.tv = new UuidView (this, ts);
      this.sw = new Gtk.ScrolledWindow (null, null);
      this.sw.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
      this.sw.add (this.tv);
      // レイアウト
      this.vbox = new Gtk.VBox (false, 0);
      this.vbox.pack_start (this.menubar, false, false, 0);
      this.vbox.pack_start (this.sw, true, true, 0);
      this.add (this.vbox);
      this.set_size_request (100, 50);
      this.resize (640, 300);
      // シグナル
      this.delete_event.connect ((ev) =>
      {
        Gtk.main_quit ();
        return false;
      });
      this.item_quit.activate.connect (Gtk.main_quit);
      this.item_copy.activate.connect (this.copy_uuid);
    }
    /**
     * アクティブなツリービュー項目に相当するTreeStore上の項目のUUID部分をクリップボードにコピー
     */
    public void
    copy_uuid ()
    {
      Gtk.TreeIter iter;
      string uuid;
      unowned Gtk.TreeModel model;
      unowned Gtk.TreeSelection selection = this.tv.get_selection ();
      selection.get_selected (out model, out iter);
      model.get (iter,
                 UuidView.Column.UUID, out uuid);
      if (uuid != null)
        this.cb.set_text (uuid, (int) uuid.length);
    }
  }
  int
  main (string[] args)
  {
    MainWindow win;
    Gtk.TreeStore ts_disks;
    Gtk.TreeIter iter_partition, iter_disk = Gtk.TreeIter (), iter = Gtk.TreeIter ();
    // デバイス情報を問い合わせるクライアント(GUdev.Clientクラス)
    var c = new GUdev.Client (null);
    // GTK+の初期化
    Gtk.init (ref args);
    // blockサブシステムの全てのデバイス情報(GUdev.Deviceオブジェクトのリスト)を得る
    GLib.List<GUdev.Device> devs = c.query_by_subsystem ("block");
    if (devs.length () == 0)
    {
      printerr ("No devices found in subsystem \"block\"\n");
      return EXIT_FAILURE;
    }
    // ツリー構造の初期化
    ts_disks = new Gtk.TreeStore (UuidView.Column.NUMBER,  // 項目数
                                  typeof (string),         // デバイス名/表示
                                  typeof (string),         // ファイルシステム名/表示
                                  typeof (string),         // UUID/表示
                                  typeof (uint64));        // オフセット/非表示
    // 各デバイスごとに処理
    foreach (GUdev.Device d in devs)
    {
      string? devname, devtype;
      devtype = d.get_devtype ();
      if (devtype == null)
        continue;
      devname = d.get_name ();
      if (devtype == "disk")
      {
        // ディスクのデバイス(sd*, hd*)
        if (devname.has_prefix ("sd") || devname.has_prefix ("hd"))
        {
          // ツリーのトップレベルにディスクのデバイスの項目を追加
          // 名前はID_MODELを使用・他の項目は空のまま
          ts_disks.append (out iter_disk, null);
          ts_disks.set (iter_disk,
                        UuidView.Column.NAME, "%s - %s".printf (d.get_name (), d.get_property ("ID_MODEL")));
        }
      }
      else if (devtype == "partition")
      {
        string name_disk;
        // 拡張パーティション(IDが0x05, 0x0f, 0x85のいずれか)は飛ばす
        if (d.get_property ("UDISKS_PARTITION_TYPE") == "0x05"
         || d.get_property ("UDISKS_PARTITION_TYPE") == "0x0f"
         || d.get_property ("UDISKS_PARTITION_TYPE") == "0x85")
          continue;
        // ディスクのデバイス名から始まる名前の場合、それにぶら下げる
        // 例: devname:"sda5" name_parent:"sda - MODELNAME"
        ts_disks.get (iter_disk,
                      UuidView.Column.NAME, out name_disk);
        if (devname.has_prefix (name_disk[0:2]))
        {
          // ディスクのツリー項目の子を全て読み込んで
          // オフセット(ディスクの先頭からの位置)値の順になるように適切な位置に追加する
          // 指し示す項目のオフセット値が現在のデバイスの値より小さければ
          // そこをinsert_after()の対象にして以下同様に繰り返していく
          // (ループを抜けたら一番オフセット値が大きな項目の後ろに追加するようにする)
          // オフセット値が現在のデバイスより大きいものに当たったら
          // それを対象にinsert_before()で入れてforeach文の次の項目へ(continue)
          Gtk.TreeIter iter_partition_new;
          bool insert_after = true;
          uint64 offset, o;
          offset = d.get_property ("UDISKS_PARTITION_OFFSET").to_uint64 ();
          if (ts_disks.iter_has_child (iter_disk))
          {
            // ディスクの項目が子を持っている場合
            ts_disks.iter_children (out iter_partition, iter_disk);  // 親から最初の子にたどる
            ts_disks.get (iter_partition,
                          UuidView.Column.OFFSET, out o);
            if (o < offset)
              iter = iter_partition;
            else if (o > offset)
            {
              ts_disks.insert_before (out iter_partition_new, iter_disk, iter_partition);
              ts_disks.set (iter_partition_new,
                            UuidView.Column.NAME, d.get_name (),
                            UuidView.Column.FSTYPE, d.get_property ("ID_FS_TYPE"),
                            UuidView.Column.UUID, d.get_property ("ID_FS_UUID"),
                            UuidView.Column.OFFSET, offset);
              continue;
            }
            else
              // 通常は同じオフセットのパーティションが複数存在することはない
              continue;
            // ディスク項目の子を次へ次へとたどっていく
            // 中の比較は上と同様
            while (ts_disks.iter_next (ref iter_partition))
            {
              ts_disks.get (iter_partition,
                            UuidView.Column.OFFSET, out o);
              if (o < offset)
                iter = iter_partition;
              else if (o > offset)
              {
                ts_disks.insert_before (out iter_partition_new, iter_disk, iter_partition);
                ts_disks.set (iter_partition_new,
                              UuidView.Column.NAME, d.get_name (),
                              UuidView.Column.FSTYPE, d.get_property ("ID_FS_TYPE"),
                              UuidView.Column.UUID, d.get_property ("ID_FS_UUID"),
                              UuidView.Column.OFFSET, offset);
                insert_after = false;
                break;
              }
              else
                // 通常は同じオフセットのパーティションが複数存在することはない
                continue;
            }  // while (ts_disks.iter_next (ref iter_partition))
            // ディスクの子に現在のデバイスのオフセット値より小さいものしかなかった場合
            // その中の最大(最後)のものの後ろに現在のデバイスを追加する
            if (insert_after)
            {
              ts_disks.insert_after (out iter_partition_new, iter_disk, iter);
              ts_disks.set (iter_partition_new,
                            UuidView.Column.NAME, d.get_name (),
                            UuidView.Column.FSTYPE, d.get_property ("ID_FS_TYPE"),
                            UuidView.Column.UUID, d.get_property ("ID_FS_UUID"),
                            UuidView.Column.OFFSET, offset);
            }
          }
          else
          {
            // まだディスクの項目に子がなければそのまま(そのディスクの項目に)ぶら下げる
            ts_disks.append (out iter_partition, iter_disk);
            ts_disks.set (iter_partition,
                          UuidView.Column.NAME, d.get_name (),
                          UuidView.Column.FSTYPE, d.get_property ("ID_FS_TYPE"),
                          UuidView.Column.UUID, d.get_property ("ID_FS_UUID"),
                          UuidView.Column.OFFSET, offset);
          }  // if (! ts_disks.iter_has_child (iter_disk))
        }  // if (devname.has_prefix (name_disk[0:2]))
      }  // else if (devtype == "partition")
    }  // foreach (GUdev.Device d in devs)
    win = new MainWindow (ts_disks);
    win.show_all ();
    Gtk.main ();
    return EXIT_SUCCESS;
  }
}

上のVala言語のソースをC言語に処理した段階のものも貼り付ける(XZ圧縮 + Base64)。
[任意]ファイル名: list-uuid-gtk.c.xz.b64 ライセンス: GPL-3 (or lator)

/Td6WFoAAATm1rRGAgAhARwAAAAQz1jM4G93FA1dABeKgCbsOh3nrQU3lAFHbARGRxrG0/L6eHTM
kbH2pTA/64veplUclzTPu8yjW9tauLVPs7OgU3lzNIYLj5TP0MUfN5RV1VbCBFqq6HQQ229YJl5M
olAuwj3u6IOBuH2/unWD21bHXHAaxH47XdyYROHbV9voD77lSd1Fl+iqMkVxssx6Yn6zTeMsrcjy
w+2E4Tgq9ePhWpp7qQWioT//5rf9ivFQqUu5PkuRLbaZDtYg++ZCYOs81Tf2W8r9ttziVmsmczta
C4xk+5JdLsH+1aWrCdPvLJLGOxxfKkdR+2sKsDzndhI0MbJEbIdIgUPGjeSk675nGS3xCuubN825
UZFM3cOocpTzRGOy9q/AnaDvQZTqgHs0LEbl83hvOl4pYn2mOro0TrHnDux5wfFz6KIBvR0yYNV2
IxTEB9wLPxpGEr4TyNWdoyNP3seSoT8ztENWfAvEY7fexWoS8zgalDqq6OuLbHZaWYw7O6V3to1R
DgJXJe9MKnnfWqWWUlx7iqGG71NHkEP6qPZVJEDotqO1GbNl3+ocKmqfUOaAxIoUcYCdKMYeTW6K
6ZLiaJrpSZGDtizphM0kHDvrXOhLNcRHEs0ifU3XdEuT6NcTGFwp6vNynr5uorVvPCIclIlSE84G
Kls3WJ1avfJEl6tsMktlmeD6XLtRIiRyzd20IZEi26k2JacS/NNgqZi8u+mH9GggBapg1OXwE3nv
nWVloGY01gIXdDw6tCVzanx+CyQXlr7JodiaNdsYY/XBQGO7HQGAuc3zgfnwOZcASaAQHyTfaE5O
Nbqv2x74SxfhDq0zHriU6Vn8ClmScez1MID3expONGBYabQouOwAOCU/S8eFR1fA6oI6D/SBqZhR
+Z2fbT7uzl9N83MPoptynBVF0wuuBWCXCU/lb/TBaj2JpKZbvO6a2GIPZBlJuwgyPG+ah/n1zcrI
B30qnOCuajbTl1ZAdhFD0/UY65wD3Vg6+zAqfuJ1jFQSmhLWZzCW47YZxWjWAYEndxZ94eTbT9Tp
RJpSaFp+jyF/bWqCX8tJffcs/6e3NkkBXyIEzcQSZfBrdiK5XQPG7h+laFWb1AihET0BE4JklYah
uRSr20L5EN+26HCox7E+b+OdskfJ+TJcPVTG7sUkq25niO174OMIZQ4E7KW1cYGsspiSPR21XPUh
EoQfjwM+RIsfjKXqCr9JEkFCl7MmcvtrMeUhSWW+NIgr5YzTLtjOoPMFelE4mDtQj2Wh6MlXxLpM
I+j3YUho6Wgf1KAKU7S0+2Ig/N/AfEd3lb3PZu34UCN+4Va+CsHW9sylCVjkqS9ADu4M6IUApOn9
jg3NOvkZJjxazT+80PA047HtEBtmGpoDFtgzUbMLAXzmdy85gwIk7HXYNFdvxSszdZMeItHIbGTc
AJEtvA/JrzfPf6zN2MIUbavRengh4/t2eZc1EXvRhk/1GOMrblPt8PhnrWH/KO9PcYOqxWcs/XB+
64bZ4aB1Z0b3AigVmsEugwbTlfkO0eynzuAVoyKpOS3qRLk4Vm0mAMi2xbK3mYlXChQY2hiElqmv
PXn+bzEZA9Wq26NUeEDy3DEms2cyy9SjGFsH51wmdq0BIxX2Gyj7y0Vpi2zH+HmrDx1ghABhs//T
qUwLIkX7u9k/YLOQl7WZNaoEDvFKS+1pCCi+FkKKeUTcA999bPqE/la8lYUQ9M3LCCmuNAVn9EwR
E0GWq+ZstTg+jd46Z0lkx7zcqAkP3Q/oDCVX++/n5ELc7u74kk2GGV7+TsBTvA50XlF61a0LDFcp
QIVh2pEDM0nuuxPBQJNYDltH9KD8nUzxlk+ybakZuor/NpI/B1GtJGWcEv6+wNgFfH9ABjEyo1Pa
6GaxZQvRZcebk1UY6kubFStwx9056UnEwjL26bV5WQhRVxma+DFiJGZeNVdvWbpVEtsFf10fDSw6
qm7UFQGbeE8KIJ/DBB6hu3okmGwklHgMbV8TrCv1DMLDKSiaQvbXykFdpeXVgxZnqyPwCQyy0BLC
MarBRpsq03PrbkglbnKLQZrNiFkIdFYTLUYeHSQNeNGgKvNshox8xyEdppVXvmtaE/3PYcmgFVZX
GcZwVvseVVmwwdAHQ/1r/AhRGHBVv2XMeZQAy070pQn2F84ZWDi0YDUHJJbQwGOAsaq/WeiRBiQl
edEF1EvwmqDw0qk7PMtSTbuqcspZtjp1BpTVjUo5VN+LJ60FtEOMIJ6MbIT2VXODTKI41C7YaGvb
5BqwqcXjyjCNMjTpERsA8xIYKcfUo2UCEMrKcfH2EAG85HK1xqcFBi3gvVVTYRckFD0uTPoSL9AS
bEPESK9p2eLtOKm1QhVjKm4gl43zb6BGbHQTNo7jicKB1Sz/l/PTYr7TZ1QDzoyvee1DMJPOokgr
VWflvsSppT9ou9EKbPllMccrvTYmBQXqP5rAKgwkPeIMkTWOPJLx9nPw+isA81V/Dx7uAYy5I9EM
VNZDf4UdNcSp791pGtq2wTn7fqNlo+NKzcZJ6bd+WerOKnLcxEdD+A+Y0YChTK+fQXW4jaw1ijBP
G0hmwqUrkL44drJ4FOu/AyRjdzAHrPcKeCmVANfvJLxIIlyJAJ0x9X3Nt6Ws/ZnP4FrWh0j/ldlC
YT3eC0rqkc/QPrNeStNpK/wmubP+l3F9Ow/v1O6pH2XoHENsneudeya0kRf2LLS6LjGQIgN4JfAE
WRGU7k7oRCP/YTBQl3jGcZ0djCnrfr/oh1TzROYGXyVQL2vOJ5YUbzjjBu6Zl1a3O3DVjSgsz8cn
LBzmNZsHFt4WUpyyDQCm4Mkvp/w9Xity+P1uVyxlXVl9J0KE/0XQcU/QzUl4Rzp4P2r9RelEpZFZ
cihp56KB9P+NGbPj7Zbv7g/IxcHY+NoZKw3kBwA/ycr+y2TxbAmMcI3+zGVOG+Lh/ySoP2v9lhmn
3DgMW15ECUdcoR+kH6qHURNCjers1bIe5t9GYPoBeF4rzG69BO9CBSgPT/+u6OIENYbtts/uFi2o
C39kMp4FSgxRa6SidNSNdYfiYIcMMDeiT5NDqwoayvQxoF4rXWk3Yq/SYCJhKS5y9IOHQB2wrFiq
N5ekBd9mfJy2eRXhiUJFXVarmuxnrkC6nELgIISBQtaUy9DuffTfITzIPYReNbqzfS58aBmC84MI
v+YJGe1EeS2KaONWYg6Cl3YccIheUJI0UCW9bRG/vuHKmwY4JVTIuh9zJzyXaGBqEDDTkW8t16X2
HLu5nqe4JIZiUAjl5Ttf6obpBe15w3MueD5XBNrLnne+zdKmSJy1cEsPBnmtTHFf3l7SAou0J9ep
uAbyy2fvcLk7fB5m2y2KwxZe7/ld+HRNVCwapcQvlk52WCqFtiz4mQwhdOMhdY+VCt5o02cr1y9p
0G2+6JIAmivphNUJLeSRLHxiunJGrJZpMViQaVlIQAi8VGiWi8mDKq+uKEdCg4p55GkmennUijrS
XeTmcsDYm4fE+jn42HgQjCJjvIWtGlSsSYKS9q4uIqx9rl3eZjiIIZ9HuRcZLksdg7kEKPWqKJgZ
rir3rrTfa1X59Y7uvD0XsD/tDsOnRI+nMf61IY3OrO0skykfcJioFyhdKhJ7eQLLQRI4Jwkx5LUN
VNA59veupL28wy1f+56NPhXwiOe/4HevNhJLMIG36fcoEDDqSv+jeeq2dEPRaWrS4YfEiRo7JKsI
tGG10XGUQ6ymLybRO4gaUV+sWvuTXpgs7eB9R7+jCFFD6UMQBOJwkFgiQK/DVVpT8cTj1a0w4chV
z6CJ4F+SXVEUuH5FgtofQ8EkcbPZe77YKuHYjGWSFrEXcKX3nYXIvksNcbU1tbw+PhTeeB/j0QkZ
Zwj4GK4Oiq9uXpS7pYF2TPJkX9Hzjh+vWxi8l9ug3AyXn24oN6IQLAZqNkqsHq7D+DVMimyZCphO
+rTmUvMrIiGPXctY0t0CYeLYturW6m1/h2ZzXBFOeZLL25BLLArLfd3Mj0P7G/igTKUGV3NtQLo8
qz04gchbTIqj6lLnrJ88cu7U2FqzgOhAOhadvOlyyWX0Lq9/sbJs8mGSESlixC58y4C85S58Y5fA
rgPdtyp08LQHGv/cjD5xtCAju54IujU5e5wIZhxBrqY4UyPIcTG2PT6F8q0yhJfRT5tOwbg3jnxq
zAQPQx86eBESARSs33LHU2YSyKbZwpTWkqgAYlTEWmTZhyVuUNnb3Unvfr3vIOS2f39ZInxgQ4ao
OVc7WnDy6WTyR8nLL1Iz7GJhQ+/kYzKBQ8WKZW6PuQ/fleDvjGJjXtyCYwUSpl5Y0y5vSVl1Fomb
dzWlKu3831F7rBNOQoCXPGpuL0PaaWR2hg6L92ZnUKYYKL/FjylBh2UzTRunWTKwJSnyHWfeuQ9L
8omVt67McMGSjq3qCRfsB2nw0J/XHjoaG3RkBymBRB9w9LiFY0yF9SCJwLM+hQg1iqpkbha0Dr8J
48GrzDkUmxypLGwzcBB0X2ukO7KKdLX+o2Fb/eMbBeDgSSlvLYVD1YFDy5W5bGxG9EXY/TLjiOsJ
ttc3cYd68lElzaqgAEj/ebvmi8YpjVd+eH0zkWAEfbxzzaLd60MRgrRil7i5lkzKYnYXMAoW+upW
zgurSDXKYRN/0NMxm/Woz+3+S2624gcMNiDPXgMR8XyU+LTP0gWfUWfErm+lTpaGfLKpVx8SDhUR
7fXf6Cs3bMrasI07LDhfpMDmqBwfMAtkz8dyyTL/Ds6dR9xPMK3JoUjpgJsaV3NSUv9cE40ImIL7
bg1qI2WIpwgiVVgNrTT2+d29Czn3H8EMuu/4wQ0knh/8GzW0acpKWG9PKaNgj3I4E+SC1nYZ3THE
+PjNnHUfuzsdIFXeM4wPMvsXYMFGbZPNMFGVkCkqz0Ce6q2/+9HNRMf+7ZrPibWOWNsRIToM/2f6
iTUtwzGFT2zXzcYfzLT0hZ6Sdqc4maDKY4pL2ULP31Khu81ABntJU3tgjA1oKVHZhzwiq5jXudgT
SiP15NDoAWpwK/IZLR87j7tslOQatimNsPZehtyJ/d9uT2bVJqyFY+eOCL0RLE+d0+AcYtxVi6h+
/+/j6/O2sj1oxAhreTE4aBvyYN6xXPP2goZ3XbX2f4dYb5GR3WLhiXPZvxATD9QrmIKMDdBqSk+W
CIlEau49K/nCesk8AMJXfctlQUeNfYT/agKqu2PGFvmtqYULxOgTgrVTwikhrSy+17OfMp4Vnza3
duj/uvHahaHHSM1QlLqK2nxwo0KsD3oXmpagwr5oqo64Qzzk/1zD09PGKmRVG1hV/4sC+f9Kwjwc
fzuqVq+EBu8gHEPtovMC7O1b6t7ePP9Fe17Ws1HnBZ5DrZx+8+zBeTtz54Q/nSV+w1TKWQ6jMUPi
5dWvVsXAklrYnCpYrquSxsvR7prjF2k2crbhhflaPJWD4iT5xNb8XvC5oZCbWTM7YFYKTfzcgk8c
yBDeuEpFMOGYjFmVTaZkqA8a5W5DlgQf5wbRKx1etJ8n+KPfg+OriKs3YCnrBJW9wHMvAk/kQoWo
Na9hBGHaSJBoS+pHERh2q4y25AhNwS6mql0u3klDJ9zQPIMRwIy9p5Qfa8zCPWEMCYrqsemcHnsr
rBBtCkvg3PxvLHzSXRxwy3AjipEYyBly903w+40I+TqLI+QDGTgtGjTnSajTVnH6RDlnd5PCHPG6
E3OWrTKpu+z8v4Y2TNK9Wbu9GhdGUb404GpjWaEcL68spG6Pg4LQdFTU47LDCbfGo9n/Hofc9yV1
lLcxh1+I9CE71haEXm45Gip2Us9g8X4p8ZiZ8f9GQJWgvZJlUcgS55uG3vAA9Yf6Vl+3NT/jdSD0
l59/nyAYsyZUHT0jAT+orDzEDB8LjCu+ssrL4VHVeajj/KXsN/WDsF0IOM5zTXFEOXSd+WrTEv7K
Dp5H7N9EOamvioa9B/CNZJ4i2zvE+qBbj04cd62+pIyzYmFZUCZzoXbkiSZpu+WKjxK4cSt7D4sR
WuZnErAl08ZYVnbGGjDyDXB9RDRLy/AQCgjHOl30p9QKFGX63WfXNnWkq8DViK7CkXMxliZeyAfc
Ms8qkRnr2sIG3kpD4QGPLT9B+LN9YqTQULsn4KmgBmdKlxgkX+iR41OezvJhk52//k7BxAzkBCuy
g2fKsjkxzFThN5hU2fUu/CB2CBpQFgpn3BRYXssTAM679y29+hfAKmCPy3YvEc3olZGJcYbsV5gI
zZNS+Mj2EldUTot06FSR9FWodi4MnHHGyb55NyMeJhlFGCBl3aRNG56aGAVdAo9l6t2jFAIVJyNu
51nPKjirOucAjdbOX72+dsKyHQmCwXqngE07aeqczLlnh5kiQ/4ofIICc3hOYXrj3Ekldn83whAA
CQLBVBIWwkyCdo1Xb2raAn1UondxjmegtYE6YyoNa/cDkv7srpOD8OOoCKSQPUHiWt4cK6e/2mhd
7hUh/i7xkDtrWnPXjRijmKSvn6Dp4bwmW9aE0VqryZ1eBL2s/H5t1uOmkLxHoMW2AQlKBVFozWD7
2R/SY1aOD/jgNYgRhoGti8Lbod/E8IBcCucxKWTGzSoVbPqvoCpRvJmBonz0cXkvz2/RfxGgwaEU
PjwLqlxthqSbYwC5wTEtk69FEl7CGoRZXZk9tFFAz+nc8tNQRQTPWhqtbJbTqbFnkWEsYSoFQPSz
I/R0VxrSgaELg6300zxaTow4HDCf7UZjXykGT8JoFnY4ikQWEhq9VGQf22SA61g8yRRrle30a/gA
jFAS4pkWRUpeN4IJYitgs2lwkNoCgx9XxNPPd4//X5QApENTS2qML/W3AZhC1D5EnVwZW7Jqsb95
c+A3s17sjY7fedkYj4EiGU9gOvBxyShf/Ap5t1fX1KvuAAAAAABQNpLExN/lTgABqSj43gEADecp
0bHEZ/sCAAAAAARZWg==

こちらはValaのない環境でも各ライブラリの開発パッケージだけあれば下のようにしてコンパイルできる。

$ base64 -d list-uuid-gtk.c.xz.b64 | unxz | gcc -O2 -Wall $(pkg-config --cflags --libs gtk+-2.0 gudev-1.0) -o list-uuid-gtk -x c -

関連記事:

使用したバージョン:

  • Vala 0.10.1
  • GTK+ 2.20.1
  • GUdev 151