シェルスクリプトマガジン

特集3 ユニケージ開発のシステム(Vol.79記載)

著者:松浦 智之

システム開発手法「ユニケージ」では、データ保存用に「ファイル」を使い、ユニケージ専用コマンド群「usp Tukubai」と「シェルスクリプト」で業務システムを開発します。usp Tukubaiは有償ソフトですが、無償のオープンソース版「Open usp Tukubai」もあります。このOpen usp Tukubaiを使って、ユニケージ開発を無料で始めてみましょう。

シェルスクリプトマガジン Vol.79は以下のリンク先でご購入できます。

図6 Apache HTTP Serverの郵便番号・住所検索システム用設定ファイル(/etc/apache2/sites-available/zip2addr.conf)

<IfModule alias_module>
    Alias /zip2addr /home/ユーザー名/ZIP2ADDR/public_html
    <Directory /home/ユーザー名/ZIP2ADDR/public_html>
         AddHandler cgi-script .cgi
         Options all
         Require all granted
    </Directory>
</IfModule>

図10 MK_ZIPTBL.SHのソースコード

#!/bin/sh -u
(略)
homd=$(d=${0%/*}/; [ "_$d" = "_$0/" ] && d='./'; cd "$d.."; pwd)
datd=$homd/DATA
shld=$homd/SHELL
webd=$homd/public_html

url_ken=https://www.post.japanpost.jp/zipcode/dl/oogaki/zip/ken_all.zip
url_jig=https://www.post.japanpost.jp/zipcode/dl/jigyosyo/zip/jigyosyo.zip

export LC_ALL=C
(略)
nocmds=''
type wget   >/dev/null 2>&1 || { nocmds="$nocmds,wget"  ; }
type gunzip >/dev/null 2>&1 || { nocmds="$nocmds,gunzip"; }
type iconv  >/dev/null 2>&1 || { nocmds="$nocmds,iconv" ; }
if [ -n "$nocmds" ]; then
  echo "${0##*/}: ${nocmds#,} not found. Install them in advance." 1>&2
  exit 1
fi
(略)
wget -q -O - "$url_ken"           |
gunzip                            |
tr -d '\r'                        |
iconv -c -f Shift_JIS -t UTF-8    |
tr -d '"'                         |
awk -F , '{print $3,$7,$8,$9}'    > $datd/ziptbl_ken.txt

if [ ! -s $datd/ziptbl_ken.txt ]; then
  echo "${0##*/}: Failed to make zip_ken.txt" 1>&2; exit 1
fi
(略)
wget -q -O - "$url_jig"           |
gunzip                            |
tr -d '\r'                        |
iconv -c -f Shift_JIS -t UTF-8    |
tr -d '"'                         |
awk -F , '{print $8,$4,$5,$6 $7}' > $datd/ziptbl_jig.txt

if [ ! -s $datd/ziptbl_jig.txt ]; then
  echo "${0##*/}: Failed to make zip_ken.txt" 1>&2; exit 1
fi
(略)
exit 0

図12 ZIP2ADDR.AJAX.cgiのコード

#!/bin/sh -u
(略)
homd=$(d=${0%/*}/; [ "_$d" = "_$0/" ] && d='./'; cd "$d.."; pwd)
datd=$homd/DATA
shld=$homd/SHELL
webd=$homd/public_html

export LC_ALL=C
(略)
exit_trap() {
  set -- ${1:-} $?  # $? is set as $1 if no argument given
  trap '' EXIT HUP INT QUIT PIPE ALRM TERM
  [ -d "${tmpd:-}" ] && rm -rf "$tmpd"
  trap -  EXIT HUP INT QUIT PIPE ALRM TERM
  exit $1
}

error500_exit() {
  echo 'Status: 500 Internal Server Error'
  echo 'Content-Type: text/plain'
  echo
  echo '500 Internal Server Error'
  echo "($@)"
  exit 1
}

error400_exit() {
  echo 'Status: 400 Bad Request'
  echo 'Content-Type: text/plain'
  echo
  echo '400 Bad Request'
  echo "($@)"
  exit 1
}
(略)
trap 'exit_trap' EXIT HUP INT QUIT PIPE ALRM TERM
tmpd=$(mktemp -d -t "_${0##*/}.$$.XXXXXXXXXXX")
[ -d "$tmpd" ] || error500_exit 'Failed to mktemp'
(略)
printf '%s\n' "${QUERY_STRING:-}" |
cgi-name                          > $tmpd/cgivars

zip=$(nameread zipcode $tmpd/cgivars)
printf '%s\n' "$zip" | grep -qE '^[0-9]{7}$' || error400_exit 'Invalid zipcode'
(略)
echo $homd/DATA/ziptbl* | grep -qF '*' && error500_exit 'No table files exist'

cat $homd/DATA/ziptbl*                 |
awk '$1=="'"$zip"'"{print "pref",$2;
                    print "city",$3;
                    print "town",$4;}' > $tmpd/address123
[ -s $tmpd/address123 ] || error400_exit 'No address found'
(略)
echo 'Content-Type: text/html; charset=utf-8'
echo 'Cache-Control: private, no-store, no-cache, must-revalidate'
echo 'Pragma: no-cache'
echo

formhame $webd/ZIP2ADDR.html $tmpd/address123 |
sed -n '/BEGIN ADDRESS123/,/END ADDRESS123/p'
(略)
[ -d "${tmpd:-}" ] && rm -rf "$tmpd"
exit 0

図13 ZIP2ADDR.htmlのコード

<!DOCTYPE html>
(略)
<script type="text/javascript" src="ZIP2ADDR.js"></script>
(略)
</head>

<body>
<h1>郵便番号→住所検索 デモ</h1>
<form action="#dummy">
<table border="0" id="addressform">
  <tr>
    <td>
      <dl>
        <dt>郵便番号</dt>
        <dd><input id="zipcode1" type="text" name="zipcode1" value="" size="3" maxlength="3" />-<input id="zipcode2" type="text" name="zipcode2" value="" size="4" maxlength="4" /></dd>
        <dd><input id="run" type="button" name="run" value="検索実行!" onclick="zip2addr();"></dd>
      </dl>
    </td>
  </tr>
  <tr>
    <td>
      <dl id="adress123">
        <!-- BEGIN ADDRESS123-->
        <dt>住所(都道府県名)</dt>
        <dd>
          <select id="pref" name="pref">
            <option value="(未選択)">(未選択)</option>
(略)
            <option value="沖縄県"  >沖縄県</option>
          </select>
        </dd>
        <dt>住所(市区町村名)</dt><dd><input id="city" type="text" size="60" name="city" value="" /></dd>
        <dt>住所(町名以降)</dt><dd><input id="town" type="text" size="60" name="town" value="" /></dd>
        <!-- END ADDRESS123-->
      </dl>
    </td>
  </tr>
</table>
</form>
</body>
</html>

図14 ZIP2ADDR.jsのコード

(略)
function zip2addr() {
  var url;
  var zipcode;
  var xhr;
(略) 
  if (! document.getElementById('zipcode1').value.match(/^([0-9]{3})$/)) {
    alert('郵便番号(前の3桁)が正しくありません');
    return;
  }
  zipcode  = RegExp.$1;
  if (! document.getElementById('zipcode2').value.match(/^([0-9]{4})$/)) {
    alert('郵便番号(後の4桁)が正しくありません');
    return;
  }
  zipcode += RegExp.$1;
(略)
  xhr = createXMLHttpRequest();
  if (xhr) {
    url  = 'ZIP2ADDR.AJAX.cgi?zipcode='+zipcode;
    url += '&dummy='+parseInt((new Date)/1);

    xhr.open('GET', url, true);
    xhr.onreadystatechange = function(){zip2addr_callback(xhr)};
    xhr.send(null);
  }
(略)
  return;
}
(略)
function zip2addr_callback(xhr) {

  var e;
(略)
  if (xhr.readyState != 4) {return;}
  if (xhr.status == 0    ) {return;}
  if      (xhr.status == 400) {
    alert('郵便番号が正しくありません');
    return;
  }
  else if (xhr.status != 200) {
    alert('アクセスエラー(' + xhr.status + ')');
    return;
  }
(略)
  e = document.getElementById('adress123');
  if (!e) {alert('エラー: 住所欄が存在しない!'); return;}
  e.innerHTML = xhr.responseText;
(略)
  return;
}