銀の弾丸

プログラミングに関して、いろいろ書き残していければと思っております。

WEBでお手軽行列演算 OCTAVE ONLINE β

「あればいいな」と検索したら、ホントにあって驚いた。

この度「ブラウザーで動くOctave」を見つけまして、軽く使ってみたのでご紹介。「そんなの常識、前からみんな使ってるよーん」ってな場合は是非ともスルーでお願いします。

そう、これはOctaveのWebアプリです。結構さくさく動いています。 サーバー側で演算してるようなので、演算量が多くても、思った以上に速いです。 MATLAB完全互換で行列演算し放題ですよ(多分)。


f:id:takamints:20150701205506p:plain

流行り(?)の機械学習やディープラーニングでは、本格的で強力な行列演算ツールが欠かせませんね。通常はローカルPCにOctaveMATLABをインストールして使っていると思いますが、「ちょっとここらで逆行列」ってな時に、ブラウザーだけで手軽に計算できますね。まだ全部の機能を試したわけではありませんので制限事項などはわかりませんが、たいていのことはできそうです。

OCTAVE ONLINE β

octave-online.net

てなことで、以下にだらだらご紹介


OCTAVE ONLINE βにアクセスすると以下の画面。タイトルですね。

タイトル画面

f:id:takamints:20150701213122p:plain

タイトル画面を右上の「✕」ボタンで閉じると、そのまま matlab / octave のコードが実行できる状態になります。

初期画面

f:id:takamints:20150701205525p:plain

右上の「Sign In」ボタンで認証すれば、スクリプトのアップロードなどが可能になるようです。 (サインインしなくても計算は出来ます)

認証はEmail(?)とGoogle+で可能。もうチョット種類があればメジャーへの道が開けるような気もしますけど、まあ大きなお世話か。

サインイン画面

f:id:takamints:20150701205522p:plain

とりあえずGoogle+で認証して、シグモイド{ g(z)=1/(1+e(-z)) }を書いてみました(見難くてすみませんクリックすれば拡大します)。

シグモイド描く

f:id:takamints:20150701205506p:plain

実行したコードは以下の三行。

octave:1> z = [-10:.1:10];
octave:2> o = ones(size(z));
octave:3> plot(z, o ./ (o + exp(o) .^ ( -1 * z)))

3行目のplotで、画面右上にグラフが描かれます。このグラフをクリックすれば、拡大表示されますし、左下の下向き矢印的な小さなボタンのクリックで、PNG画像のダウンロードが可能です。グラフを閉じるには左下の斜めの矢印です。

拡大表示

f:id:takamints:20150701205510p:plain

以下はダウンロードしたPNG画像です。

plotをダウンロードしたPNG画像

f:id:takamints:20150701155524p:plain

数回ですが、演算結果を得るまで待たされる場合がありました。 10秒程度の待ち秒数がカウントダウン表示されていました。 サーバー側で負荷調整とかしているのかもしれません。

あと、表示する結果が大きすぎたのかもしれませんが、タイムアウトみたいになったことも一度ありました。

サインインしたまま放置していると、自動的にログアウトするようです。 これもサーバー側の都合かもしれません。


OCTAVE ONLINE β

octave-online.net


Octave教科書 (I・O BOOKS)
Octave教科書 (I・O BOOKS)
posted with amazlet at 15.07.01
赤間 世紀
工学社
売り上げランキング: 467,825

MATLABプログラミング入門
上坂 吉則
牧野書店
売り上げランキング: 63,036

ラズパイにシャットダウンボタンを付けました

買っててよかったブレッドボード。

↑これで一通りそろいますね。


f:id:takamints:20150613163317p:plain

ブレッドボードに会社の人から頂いたタクトスイッチを乗せ、ジャンパー線でラズパイのGPIOとつないだら、回路(というほどのものではないですが)は完成。 ソフト側では、スイッチ入力を監視して、シャットダウンを行うデーモン作って仕込んで出来上がり。

ブレッドボードの配線図やらコードは、全部GitHub(↓)に置いておりますので、ご自由にどうぞ(MIT LICENSE)。

github.com

README 抜粋

RapidShutdown - Raspberry Pi Shutdown Daemon

ラズパイのGPIOに接続したボタンからシャットダウンを行うデーモンです。

ボタンは長押しとかではありません。押した瞬間に shutdown -h now します。当然のごとく、キャンセルはできません。

ビルドとセットアップ

以下を実行してビルドして自動起動のデーモン shutdownd を設定します。

git clone https://github.com/takamin/RapidShutdown.git
cd button-down
./init-submodules.sh
mkdir build
cd build
cmake ..
make
cd ..
./setupdatemon.sh

配線図

GPIOの25番からスイッチを接続。もう一方の端子はGNDへ。

  • この図のラズパイは Model B(R2) です。他のモデルの場合は、ピン配置が違うかもしれませんので気をつけてください。
  • GPIOの25を入力ポートとして使用しています。他のポートに変更する場合は、shutdownd.cを直接書き換えてください。
  • とりあえずブレッドボードとタクトスイッチを使っていますが、なければGPIOの25とGNDをショートさせてもシャットダウンします。
  • GNDはどのGNDでもかまいません。よく見ると25番のとなりにGNDがありますね。ここに直接付けられるスイッチがあれば良いですね。

その他

サブモジュール

以下のサブモジュールを使用しています。


現物写真

まあ、余計なものがいっぱい乗っていますが、こういう感じでやっています。ジャンパーが足りなくて、涙ぐましい創意工夫とかねw。

f:id:takamints:20150613174525j:plain

まとめと反省、所感・雑感

思った以上に便利でした。気軽に電源を入れられるようになりました。

普段、キーボードもディスプレイもつなげていませんから、今まで電源を切るには ssh で入ってから sudo shutdown -h now とやってたわけですが、スイッチ押すだけで電源が切れる(自動では無理ですが)。今まで、シャットダウンするのが手間で、ちょっと出かけてる間、電源入れっぱなしにしてたとか、ブチッと切った事も何度かありますからね。楽は善。

ライセンスについて

ライセンスは例によって、お手軽な MIT LICENCEですが、サブモジュールとして使用している WiringPi が LGPLv3 ですから、ご留意ください。

デーモン化について

デーモン化については、前回記事のGistを、これまたGitHubのリポジトリに昇格(?)させて利用しています。

ブレッドボードの配線について

配線図は、Fritzingというフリーのツールを使って書きました。便利ですね。

入力はいつもプルダウン

シャットダウンスイッチをつなげている、GPIOの入力ポートは、プルダウンしています。つまりスイッチを押したら LOW レベルが読めるということですね。

チャタ取りとか

とにかく簡単にするために、チャタリング対策はやってませんが、本当はやるべきだと思います。それから、長押しでシャットダウンシーケンス発動とか、同じボタンでキャンセルも、できれば良いなと思っては、います。誤動作などで痛い目にあったり、気が向いたらやるかもしれません。

関連リンク・参考

github.com

takamints.hatenablog.jp


ちょっとリッチなこういう(↓)セットも。

Raspberry Pi電子工作エントリーキット(Economy)
TechShare
売り上げランキング: 2,095


ラズパイで自動起動するデーモンを自作する

f:id:takamints:20150607205740p:plain

Raspberry Piで動作するデーモンをC言語で作る方法と、自動起動する設定手順などをまとめました。



はじめに

以下の順に説明します。C言語そのものや、コンパイルとかリンクに関する説明はしていません。

  1. デーモン本体の作成
  2. デーモン起動/停止スクリプトの作成
  3. 実行モジュール・スクリプトの配置と自動起動の設定
  4. 動作の確認
  5. まとめと反省・所感や雑感

説明中で使用しているコードは、Gist「C言語でラズパイのデーモンを作るときの補助関数とスケルトン · GitHub」に置いていますので、実際に動作させる場合はそちらを参考になさってください。


RASPBERRY DREAM
RASPBERRY DREAM
posted with amazlet at 15.06.08
avex trax (2012-09-07)
売り上げランキング: 52,713

1. デーモン本体の作成

デーモンとして動作するプログラムを作成するには、以下の4つの機能仕様を実装します。 2と3は必須条件ではないですがいろんな理由でやっておいたほうがいいと思います。

  1. デーモンプロセスを生成する
  2. ログはsyslogで出力する
  3. プロセスIDをファイルへ保存する
  4. SIGTERMで終了する

1~3については、Gist の daeomonize.cdaemonizeという関数にまとめています。デーモンのmainは同Gistのmydaemon.cを参考にしてください。

1-1. デーモンプロセスを生成する

デーモンは親プロセスを持たないプロセスです。プログラム起動後、子プロセスを生成してから終了することで、子プロセスがデーモンとして動き続けます。 最近のLinuxには、デーモンを生成するための daemon() という関数が用意されており、簡単に生成できるようになっています。

daemon()を呼び出したプロセスは、forkしてから終了します。ですからdaemon() から戻るのは子プロセスだけ。この時点で既にデーモンが生成されて実行している状態になっているんですね。

(Man page of DAEMON より抜粋)
名前
daemon - バックグラウンドで動作させる
書式
#include <unistd.h>
int daemon(int nochdir, int noclose);
説明
nochdir が 0 の場合、 daemon() は呼び出したプロセスの現在の作業ディレクトリをルートディレクトリ (“/”) に変更する。 それ以外の場合、現在の作業ディレクトリは変更されない。
noclose が 0 の場合、 daemon() は標準入力・標準出力・標準エラーを /dev/null にリダイレクトする。 それ以外の場合、これらのファイルディスクリプターは変更されない。
返り値
成功した場合、 daemon() は 0 を返す。 エラーが起こった場合、 daemon() は -1 を返す。

1-2. ログはsyslogで出力する

デーモンの標準入出力ファイルはおそらく閉じられていますので、コンソールへの表示はできません。 不具合が生じた場合に問題特定のため、以下のようにして、syslogでログを出力する準備をしておきます。

#include <syslog.h>

/* /var/log/daemon.log へ プロセスID込みでログ出力 */
openlog("daemon", LOG_PID, LOG_DAEMON));

1-3. プロセスIDをファイルへ保存する

上述の daemon 呼び出し以後に、生成されたデーモンのプロセスID(pid)をファイルに保存します。 これはデーモンの起動/停止スクリプトから利用されます。なくても何とかなりますが、停止やシャットダウン時に必要以上に時間がかかるようになってしまいます。

ファイルは慣習的に /var/run/<デーモン名>.pid とします。つまり、デーモンの実行モジュールの名前が、mydaemon なら /var/run/mydaemon.pid へそのpidが書き込まれます。

FILE* pidfile = fopen("/var/run/mydaemon.pid", "w+");
if (pidfile) {
  int pid = getpid();
  fprintf(pidfile, "%d\n", pid);
  fclose(pidfile);
} else {
  syslog(LOG_ERR,
    "daemonize() : failed to write pid.\n");
}

1-4. SIGTERMで終了する

前項までの処理が行えているなら、あとはデーモン本来の処理を行うのみです。 一般的には、SIGTERMを受け取るまでのループになると思います。

たとえば以下のような実装です。

#include <unistd.h>
#include <signal.h>
#include <syslog.h>

volatile int sigterm = 0;
void handle_sigterm(int sig) { sigterm = 1; }
int mydaemon( void ) {
  syslog(LOG_INFO, "mydaemon started.\n");
  signal(SIGTERM, handle_sigterm);
  while(!sigterm) {
    /* こんな感じ */
    usleep(1000000);
  }
  syslog(LOG_INFO, "mydaemon stopped.\n");
  return 0;
}

このほか、慣習として、SIGHUP(ハングアップ)を受け取ったら設定ファイルなどを読み直して再実行するようにするらしいです。

2. デーモン起動/停止スクリプトの作成

デーモン起動/停止スクリプトは、スケルトン(/etc/init.d/skeleton)をコピーして書き換えます。

pi@raspberrypi~ $ cp /etc/init.d/skeleton /etc/init.d/mydaemon

書き換え前:

#! /bin/sh
### BEGIN INIT INFO
# Provides:          skeleton
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Example initscript
# Description:       This file should be used to construct scripts to be
#                    placed in /etc/init.d.
### END INIT INFO

# Author: Foo Bar <foobar@baz.org>
#
# Please remove the "Author" lines above and replace them
# with your own name if you copy and modify this script.

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Description of the service"
NAME=daemonexecutablename
DAEMON=/usr/sbin/$NAME
DAEMON_ARGS="--options args"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

まず書き換えるのは、3行目あたりのコメントで# Provides: skeleton となっているところで、デーモンの名前mydaemonに書き換えます。サービスをあらわす一意な名前として扱われるようで、他のサービスとかぶっているといけないようです。(2015-06-08 追記)

次に書き換えるのが21行目あたりの NAME=daemonexecutablenameとなっているところ。これも自作デーモンの名前 mydaemon に書き換えます。

DAEMONはデーモン本体のパスです。/usr/sbin/$NAME となっていますが、前項では実行モジュールを /usr/local/sbin へ配置しましたので、/usr/local/sbin/$NAME に書き換えます。

次のDAEMON_ARGSは起動時にデーモンへ渡されるコマンドライン引数です。不要なら空にしておいたほうが気持ちよいです。

後は確認です。PIDFILEは、Cのソースに記述したpidを保存するファイルと同じであること、SCRIPTNAMEが、この起動スクリプトのファイル名と一致することを確認します。

書き換え後:

#! /bin/sh
### BEGIN INIT INFO
# Provides:          mydaemon
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Example initscript
# Description:       This file should be used to construct scripts to be
#                    placed in /etc/init.d.
### END INIT INFO

# Author: Foo Bar <foobar@baz.org>
#
# Please remove the "Author" lines above and replace them
# with your own name if you copy and modify this script.

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh scriptPATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Description of the service"
NAME=mydaemon
DAEMON=/usr/local/sbin/$NAME
DAEMON_ARGS=""
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

※ ここで PIDFILE のファイル名の設定があるから、てっきりプロセスIDを保存してくれるのだろうと思いこんでしまったです。スクリプトではチェックするだけなんですね。

3. 実行モジュール・スクリプトの配置と自動起動の設定

配置とパーミッション

実行モジュール(デーモン本体)は /usr/local/sbin へ、デーモン起動スクリプトは、/etc/init.d へ配置し、どちらも、所有者、グループ共にrootとして、実行属性を与えます。

自動起動の設定

この時点でデーモンは動作するはずですが、システム起動時に自動起動するには、update-rc.d コマンドで設定しなくてはなりません。(どうやら最近のトレンド(?)としては、insserv コマンドでやるほうが良いらしいですが、何がどう違うのか知りません)

以下は、これらの処理を一括して行うスクリプトの例です。それぞれ個別にやってもかまいませんし、本来はmakeやcmakeでやるべきなんでしょうね。あとinstallコマンドとか使うほうがスマートかと思います。が、まあとりあえず。

#!/bin/sh
NAME=mydaemon
DSCR=/etc/init.d/${NAME}
DEXE=/usr/local/sbin/${NAME}

sudo ${DSCR} stop

sudo cp ./${NAME}.sh ${DSCR}
sudo chown root.root ${DSCR}
sudo chmod 755 ${DSCR}

sudo cp ${NAME}.out ${DEXE}
sudo chown root.root ${DEXE}

sudo update-rc.d ${NAME} defaults

sudo ${DSCR} start

4. 動作確認

以上で、ラズパイを再起動すれば自作デーモンが立ち上がるはずですが、その前に確認してみましょう。

pi@raspberry ~ $ /etc/init.d/mydaemon start # 起動

pi@raspberry ~ $ /etc/init.d/mydaemon stop  # 停止

正しく起動したなら、/var/run/mydaemon.pid にそのプロセスidが保存されているはずです。catやpsで確認してみましょう。

停止に関して、数十秒処理が戻らない場合は、プロセスidが正しく記録されていない、または起動していない可能性があります(処理内容にもよりますが)。

いずれにせよ、/var/log/daemon.log にログが出ているはずですので、そちらを確認してみましょう。

問題なく動いているようなら、sudo shutdown -r now で再起動!

自動起動していると思います。

5. まとめと反省・所感や雑感

ラズパイ買って当初はGPIOで「Lチカ(LEDチカチカさせる)」やって喜んだのですが、その後大して何にもやってなかったんですよね。今回、会社の有志の勉強会でちょっくら使うことになりまして、調べながらがんばってみました。

なので、断定的に書いていますが、それほど詳しいわけではありませんので、変なところや非常識なところがあればコメントください。

実際作ってみるまで、デーモンって、WindowsのサービスやMS-DOSの常駐プログラム(懐かしw)の感覚もあって、かなり難しそうに思っていましたが、意外に簡単でした。

他のLinuxディストリビューションでも同じ方法でOKなように思いますが、とりあえず未確認なのでラズパイ限定としておきます。

さて、これでやっと、シャットダウンスイッチを作れます。

で、作りました↓

takamints.hatenablog.jp


CMakeでジェネレータを判定して分岐する

f:id:takamints:20151003145058p:plain

GCCMinGW と Raspberry Pi 両方で動くプログラムを書いていて、リンクするライブラリは切り替えたいという状況。

色々調べて何とかなったので書いておきます。もっと良い判定方法があるかもしれないですけど、とりあえず。


CMakeLists.txt : ジェネレータの名前に MinGW が含まれていたら bar もリンク

gist.github.com

要点

  • if(条件式) ~ elseif() ~ else() ~ endif() が使える。
  • elseif, else, endif の条件式は無視されるので空でよい。
  • MATCHES は正規表現のマッチングを行う演算子

参考

使える演算子がまとまっていました(↓)

qiita.com


いやしかし、CMake。

便利なんだけど複雑で情報量が少ないですね。

やりたいことの書き方を調べるのに結構苦労します。


Mastering Cmake
Mastering Cmake
posted with amazlet at 15.05.26
Ken Martin Bill Hoffman
Kitware, Incorporated
売り上げランキング: 82,100

Visual C/C++用getoptでPOSIX的コマンドラインオプション解析

苦しんで覚えるC言語
苦しんで覚えるC言語
posted with amazlet at 15.04.27
MMGames
秀和システム
売り上げランキング: 3,709

f:id:takamints:20150427230832p:plain

POSIXのgetoptは文化遺産コマンドラインオプション解析の標準ですから。Windowsでは argc と argv を直接解析しがちですが、途端にmainがカオスになってしまうんですよね。

カオスになりつつ「まあいいか」で、増改築を繰り返していると、そのうち「モウダメダー...ボクハイマナニヲヤッテルンダー」的な状況に・・・

てなことで、怒りに任せてPOSIX互換のWindows向けgetoptをゼロから書きましたので、GitHubで公開(以下リンク)してます。 どうぞご自由にお使いください。

github.com

  • 長いオプションのgetopt_long_onlyには今のところ対応していません。
  • そもそも、Windowsのコンソールアプリ用に、よく使うルーチンをまとめておこうとリポジトリを作ったのですが、意外にこれ以上のモノがないという...。

getopt.h

/*
 * getopt - POSIX like getopt for Windows console Application
 *
 * win-c - Windows Console Library
 * Copyright (c) 2015 Koji Takami
 * Released under the MIT license
 * https://github.com/takamin/win-c/blob/master/LICENSE
 */
#ifndef _GETOPT_H_
#define _GETOPT_H_

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

    int getopt(int argc, char* const argv[],
            const char* optstring);

    extern char *optarg;
    extern int optind, opterr, optopt;

#define no_argument 0
#define required_argument 1
#define optional_argument 2

    struct option {
        const char *name;
        int has_arg;
        int* flag;
        int val;
    };

    int getopt_long(int argc, char* const argv[],
            const char* optstring,
            const struct option* longopts, int* longindex);
/****************************************************************************
    int getopt_long_only(int argc, char* const argv[],
            const char* optstring,
            const struct option* longopts, int* longindex);
****************************************************************************/
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GETOPT_H_

getopt.c

/*
 * getopt - POSIX like getopt for Windows console Application
 *
 * win-c - Windows Console Library
 * Copyright (c) 2015 Koji Takami
 * Released under the MIT license
 * https://github.com/takamin/win-c/blob/master/LICENSE
 */
#include <stdio.h>
#include <string.h>
#include "getopt.h"

char* optarg = 0;
int optind = 1;
int opterr = 1;
int optopt = 0;

int postpone_count = 0;
int nextchar = 0;

static void postpone(int argc, char* const argv[], int index) {
    char** nc_argv = (char**)argv;
    char* p = nc_argv[index];
    int j = index;
    for(; j < argc - 1; j++) {
        nc_argv[j] = nc_argv[j + 1];
    }
    nc_argv[argc - 1] = p;
}
static int postpone_noopt(int argc, char* const argv[], int index) {
    int i = index;
    for(; i < argc; i++) {
        if(*(argv[i]) == '-') {
            postpone(argc, argv, index);
            return 1;
        }
    }
    return 0;
}
static int _getopt_(int argc, char* const argv[],
        const char* optstring,
        const struct option* longopts, int* longindex)
{
    while(1) {
        int c;
        const char* optptr = 0;
        if(optind >= argc - postpone_count) {
            c = 0;
            optarg = 0;
            break;
        }
        c = *(argv[optind] + nextchar);
        if(c == '\0') {
            nextchar = 0;
            ++optind;
            continue;
        }
        if(nextchar == 0) {
            if(optstring[0] != '+' && optstring[0] != '-') {
                while(c != '-') {
                    /* postpone non-opt parameter */
                    if(!postpone_noopt(argc, argv, optind)) {
                        break; /* all args are non-opt param */
                    }
                    ++postpone_count;
                    c = *argv[optind];
                }
            }
            if(c != '-') {
                if(optstring[0] == '-') {
                    optarg = argv[optind];
                    nextchar = 0;
                    ++optind;
                    return 1;
                }
                break;
            } else {
                if(strcmp(argv[optind], "--") == 0) {
                    optind++;
                    break;
                }
                ++nextchar;
                if(longopts != 0 && *(argv[optind] + 1) == '-') {
                    char const* spec_long = argv[optind] + 2;
                    char const* pos_eq = strchr(spec_long, '=');
                    int spec_len = (pos_eq == NULL ? strlen(spec_long) : pos_eq - spec_long);
                    int index_search = 0;
                    int index_found = -1;
                    const struct option* optdef = 0;
                    while(longopts->name != 0) {
                        if(strncmp(spec_long, longopts->name, spec_len) == 0) {
                            if(optdef != 0) {
                                if(opterr) {
                                    fprintf(stderr, "ambiguous option: %s\n", spec_long);
                                }
                                return '?';
                            }
                            optdef = longopts;
                            index_found = index_search;
                        }
                        longopts++;
                        index_search++;
                    }
                    if(optdef == 0) {
                        if(opterr) {
                            fprintf(stderr, "no such a option: %s\n", spec_long);
                        }
                        return '?';
                    }
                    switch(optdef->has_arg) {
                        case no_argument:
                            optarg = 0;
                            if(pos_eq != 0) {
                                if(opterr) {
                                    fprintf(stderr, "no argument for %s\n", optdef->name);
                                }
                                return '?';
                            }
                            break;
                        case required_argument:
                            if(pos_eq == NULL) {
                                ++optind;
                                optarg = argv[optind];
                            } else {
                                optarg = (char*)pos_eq + 1;
                            }
                            break;
                    }
                    ++optind;
                    nextchar = 0;
                    if(longindex != 0) {
                        *longindex = index_found;
                    }
                    if(optdef->flag != 0) {
                        *optdef->flag = optdef->val;
                        return 0;
                    }
                    return optdef->val;
                }
                continue;
            }
        }
        optptr = strchr(optstring, c);
        if(optptr == NULL) {
            optopt = c;
            if(opterr) {
                fprintf(stderr,
                        "%s: invalid option -- %c\n",
                        argv[0], c);
            }
            ++nextchar;
            return '?';
        }
        if(*(optptr+1) != ':') {
            nextchar++;
            if(*(argv[optind] + nextchar) == '\0') {
                ++optind;
                nextchar = 0;
            }
            optarg = 0;
        } else {
            nextchar++;
            if(*(argv[optind] + nextchar) != '\0') {
                optarg = argv[optind] + nextchar;
            } else {
                ++optind;
                if(optind < argc - postpone_count) {
                    optarg = argv[optind];
                } else {
                    optopt = c;
                    if(opterr) {
                        fprintf(stderr,
                            "%s: option requires an argument -- %c\n",
                            argv[0], c);
                    }
                    if(optstring[0] == ':' ||
                        (optstring[0] == '-' || optstring[0] == '+') &&
                        optstring[1] == ':')
                    {
                        c = ':';
                    } else {
                        c = '?';
                    }
                }
            }
            ++optind;
            nextchar = 0;
        }
        return c;
    }

    /* end of option analysis */

    /* fix the order of non-opt params to original */
    while((argc - optind - postpone_count) > 0) {
        postpone(argc, argv, optind);
        ++postpone_count;
    }

    nextchar = 0;
    postpone_count = 0;
    return -1;
}

int getopt(int argc, char* const argv[],
            const char* optstring)
{
    return _getopt_(argc, argv, optstring, 0, 0);
}
int getopt_long(int argc, char* const argv[],
        const char* optstring,
        const struct option* longopts, int* longindex)
{
    return _getopt_(argc, argv, optstring, longopts, longindex);
}
/********************************************************
int getopt_long_only(int argc, char* const argv[],
        const char* optstring,
        const struct option* longopts, int* longindex)
{
    return -1;
}
********************************************************/

関連記事

takamints.hatenablog.jp


C++プログラミング入門
グレゴリー サティア ダウグ ブラウン
オライリー・ジャパン
売り上げランキング: 67,633