ときどきの雑記帖 RE* (新南口)
The Sky Crawlers
星巡る方舟
BS12で次の日曜日(11/21)の夜に 宇宙戦艦ヤマト2199 星巡る方舟 が放送予定なのだけど、結構好きな作品だったりする。 2199の帰路に起こった本編にはなかったエピソードというもので ガトランティスも出てきてなかなか楽しい (そしてどうしてああなった2202…)
さらにさらに来月(12/19)放送予定の 東京ゴッドファーザーズ | 日曜アニメ劇場 | 無料アニメ番組 | BS無料放送ならBS12(トゥエルビ) が待ち遠しい。
グランデブックタワー集中レジ化
数日前にブックタワーに行って知ったのだけど、グランデとブックタワーも フロアごとにあったレジを1Fに集中させるらしい。 そうした方が効率が良いのだろうとはわかるんだけど、 あまり好きじゃないんだよな(待機列が長くなるから)
Line formats (diff)
diffで変更後の行だけをいい感じに出力する方法 - Qiita
という記事で --unchanged-line-format
, --old-line-format
, --new-line-format
なるオプションが出てきたのだけど
そんなオプションあったっけ?
と疑問に思ったのでまずはコマンドのヘルプを出力してみる。
kbk@toybox4:/mnt/c/Users/kbk$ diff --help
Usage: diff [OPTION]... FILES
Compare FILES line by line.
Mandatory arguments to long options are mandatory for short options too.
--normal output a normal diff (the default)
-q, --brief report only when files differ
-s, --report-identical-files report when two files are the same
-c, -C NUM, --context[=NUM] output NUM (default 3) lines of copied context
-u, -U NUM, --unified[=NUM] output NUM (default 3) lines of unified context
-e, --ed output an ed script
-n, --rcs output an RCS format diff
-y, --side-by-side output in two columns
-W, --width=NUM output at most NUM (default 130) print columns
--left-column output only the left column of common lines
--suppress-common-lines do not output common lines
以下かなり長いので省略
見当たらない。ひょっとしてGNU diffにはないオプション? と思いつつマニュアル 13.1 Options to diff をみると見つかった。
–new-group-format=format
Use format to output a group of lines taken from just the second file in if-then-else format. See Line Group Formats.
–new-line-format=format
Use format to output a line taken from just the second file in if-then-else format. See Line Formats.
–old-group-format=format
Use format to output a group of lines taken from just the first file in if-then-else format. See Line Group Formats.
–old-line-format=format
Use format to output a line taken from just the first file in if-then-else format. See Line Formats.
–unchanged-group-format=format
Use format to output a group of common lines taken from both files in if-then-else format. See Line Group Formats.
–unchanged-line-format=format
Use format to output a line common to both files in if-then-else format. See Line Formats.
なるほど。
出力フォーマットの指定に関しては 2.6.2 Line Formats に。
空文字列を置換
色々なもので試してみたが いきなりJavaScriptでつまづいた(笑)
>deno
Deno 1.13.1
exit using ctrl+d or close()
> "abc".replace('', '@')
"@abc"
> const re=new RegExp('','g')
undefined
> "abc".replace(re,'@')
"@a@b@c@"
>
上記の例では問題ないのだけど、
実は最初 //
でやろうとしてうまくいかなかった。
なぜかというとこれコメントの開始記号列と被るのな😄
> "abc".replace(//g, "@")
exit using ctrl+d or close()
空文字列にもマッチするパターンでもやってみる。
> "abc".replace(/x*/g, "@")
"@a@b@c@"
rubyでの結果。
>irb --nocolorize
irb(main):001:0> "abc".gsub(//, "@")
=> "@a@b@c@"
kbk@toybox4:/mnt/c/Users/kbk$ echo -n abc | perl -pe 's//@/g'
@a@b@c@kbk@toybox4:/mnt/c/Users/kbk$
>python
Python 3.9.2 (tags/v3.9.2:1a79785, Feb 19 2021, 13:44:55) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> re.sub('', '@', 'abc')
'@a@b@c@'
>>>
busybox
ところでbusyboxのawkとsedのコードを読んでたら わりとこの辺の扱いがわかりやすかった。 ので紹介。
busybox/awk.c at master · mirror/busybox
if (eo == so) {
/* Empty match (e.g. "b*" will match anywhere).
* Advance by one char. */
//BUG (bug 1333):
//gsub(/\<b*/,"") on "abc" will reach this point, advance to "bc"
//... and will erroneously match "b" even though it is NOT at the word start.
//we need REG_NOTBOW but it does not exist...
//TODO: if EXTRA_COMPAT=y, use GNU matching and re_search,
//it should be able to do it correctly.
/* Subtle: this is safe only because
* qrealloc allocated at least one extra byte */
resbuf[residx] = *sp;
if (*sp == '\0')
goto ret;
sp++;
residx++;
}
busybox/sed.c at master · mirror/busybox
/* If matched string is empty (f.e. "c*" pattern),
* copy verbatim one char after it before attempting more matches
*/
prev_match_empty = (start == end);
if (prev_match_empty) {
if (!line[end]) {
tried_at_eol = 1;
} else {
pipe_putc(line[end]);
end++;
}
}
glob zsh 15
前回、
前々回
でscanner
から呼び出す insert
が云々という話になっていたのでその辺から。
zsh/glob.c at 00d20ed15e18f5af682f0daec140d6b8383c479a · zsh-users/zsh
関数の直前にファイルスコープの inserts
(こっちは複数形)という変数があって、
この変数を使って
関数をまたいだ情報のやり取りをしている。
/* This may be set by qualifier functions to an array of strings to insert
* into the list instead of the original string. */
static char **inserts;
そして関数本体。 qulaifier関連の処理で長くなっている感じ?
/* add a match to the list */
/**/
static void
insert(char *s, int checked)
{
struct stat buf, buf2, *bp;
char *news = s;
int statted = 0;
queue_signals();
inserts = NULL;
if (gf_listtypes || gf_markdirs) {
/* Add the type marker to the end of the filename */
mode_t mode;
if (statfullpath(s, &buf, 1)) {
unqueue_signals();
return;
}
else {
checked = statted = 1;
}
mode = buf.st_mode;
if (gf_follow) {
if (!S_ISLNK(mode) || statfullpath(s, &buf2, 0))
memcpy(&buf2, &buf, sizeof(buf));
statted |= 2;
mode = buf2.st_mode;
}
if (gf_listtypes || S_ISDIR(mode)) {
int ll = strlen(s);
news = (char *) hcalloc(ll + 2);
strcpy(news, s);
news[ll] = file_type(mode);
news[ll + 1] = '\0';
}
}
if (qualct || qualorct) {
/* Go through the qualifiers, rejecting the file if appropriate */
struct qual *qo, *qn;
if (!statted && statfullpath(s, &buf, 1)) {
unqueue_signals();
return;
}
news = dyncat(pathbuf, news);
statted = 1;
qo = quals;
for (qn = qo; qn && qn->func;) {
g_range = qn->range;
g_amc = qn->amc;
g_units = qn->units;
if ((qn->sense & 2) && !(statted & 2)) {
/* If (sense & 2), we're following links */
if (!S_ISLNK(buf.st_mode) || statfullpath(s, &buf2, 0))
memcpy(&buf2, &buf, sizeof(buf));
statted |= 2;
}
bp = (qn->sense & 2) ? &buf2 : &buf;
/* Reject the file if the function returned zero *
* and the sense was positive (sense&1 == 0), or *
* vice versa. */
if ((!((qn->func) (news, bp, qn->data, qn->sdata))
^ qn->sense) & 1) {
/* Try next alternative, or return if there are no more */
if (!(qo = qo->or)) {
unqueue_signals();
return;
}
qn = qo;
continue;
}
qn = qn->next;
}
} else if (!checked) {
if (statfullpath(s, NULL, 1)) {
unqueue_signals();
return;
}
news = dyncat(pathbuf, news);
} else
news = dyncat(pathbuf, news);
その辺の処理をすませたあとで 次のwhileループがあるのだけど
while (!inserts || (news = dupstring(*inserts++))) {
if (colonmod) {
/* Handle the remainder of the qualifier: e.g. (:r:s/foo/bar/). */
char *mod = colonmod;
modify(&news, &mod, 1);
}
if (!statted && (gf_sorts & GS_NORMAL)) {
statfullpath(s, &buf, 1);
statted = 1;
}
if (!(statted & 2) && (gf_sorts & GS_LINKED)) {
if (statted) {
if (!S_ISLNK(buf.st_mode) || statfullpath(s, &buf2, 0))
memcpy(&buf2, &buf, sizeof(buf));
} else if (statfullpath(s, &buf2, 0))
statfullpath(s, &buf2, 1);
statted |= 2;
}
matchptr->name = news;
if (statted & 1) {
matchptr->size = buf.st_size;
matchptr->atime = buf.st_atime;
matchptr->mtime = buf.st_mtime;
matchptr->ctime = buf.st_ctime;
matchptr->links = buf.st_nlink;
#ifdef GET_ST_ATIME_NSEC
matchptr->ansec = GET_ST_ATIME_NSEC(buf);
#endif
#ifdef GET_ST_MTIME_NSEC
matchptr->mnsec = GET_ST_MTIME_NSEC(buf);
#endif
#ifdef GET_ST_CTIME_NSEC
matchptr->cnsec = GET_ST_CTIME_NSEC(buf);
#endif
}
if (statted & 2) {
matchptr->_size = buf2.st_size;
matchptr->_atime = buf2.st_atime;
matchptr->_mtime = buf2.st_mtime;
matchptr->_ctime = buf2.st_ctime;
matchptr->_links = buf2.st_nlink;
#ifdef GET_ST_ATIME_NSEC
matchptr->_ansec = GET_ST_ATIME_NSEC(buf2);
#endif
#ifdef GET_ST_MTIME_NSEC
matchptr->_mnsec = GET_ST_MTIME_NSEC(buf2);
#endif
#ifdef GET_ST_CTIME_NSEC
matchptr->_cnsec = GET_ST_CTIME_NSEC(buf2);
#endif
}
matchptr++;
if (++matchct == matchsz) {
matchbuf = (Gmatch)zrealloc((char *)matchbuf,
sizeof(struct gmatch) * (matchsz *= 2));
matchptr = matchbuf + matchct;
}
if (!inserts)
break;
}
unqueue_signals();
return;
}
whileの条件部分やループ終端でのbreak判定で 登場する insertsを操作している部分がループ内に見当たらない。 じゃあどこでそれをという話になって、 それは qualsheval という関数。
zsh/glob.c at 00d20ed15e18f5af682f0daec140d6b8383c479a · zsh-users/zsh
/* evaluate a string */
/**/
static int
qualsheval(char *name, UNUSED(struct stat *buf), UNUSED(off_t days), char *str)
{
Eprog prog;
if ((prog = parse_string(str, 0))) {
int ef = errflag, lv = lastval, ret;
int cshglob = badcshglob;
unsetparam("reply");
setsparam("REPLY", ztrdup(name));
badcshglob = 0;
execode(prog, 1, 0, "globqual");
if ((ret = lastval))
badcshglob |= cshglob;
/* Retain any user interrupt error status */
errflag = ef | (errflag & ERRFLAG_INT);
lastval = lv;
if (!(inserts = getaparam("reply")) &&
!(inserts = gethparam("reply"))) {
char *tmp;
if ((tmp = getsparam("reply")) || (tmp = getsparam("REPLY"))) {
static char *tmparr[2];
tmparr[0] = tmp;
tmparr[1] = NULL;
inserts = tmparr;
}
}
return !ret;
}
return 0;
}
なのだけど、実はこの関数直接は呼び出されなくて 関数ポインター経由で呼ばれる。 その設定をしているのがここ (けっこう呼び出しツリーをさかのぼるけど)。
zsh/glob.c at 00d20ed15e18f5af682f0daec140d6b8383c479a · zsh-users/zsh
case '+':
case 'e':
{
char *tt;
tt = glob_exec_string(&s);
if (tt == NULL) {
data = 0;
} else {
func = qualsheval;
sdata = tt;
}
break;
とここ。
zsh/glob.c at 00d20ed15e18f5af682f0daec140d6b8383c479a · zsh-users/zsh
if (func) {
/* Requested test is performed by function func */
if (!qn)
qn = (struct qual *)hcalloc(sizeof *qn);
if (ql)
ql->next = qn;
ql = qn;
if (!newquals)
newquals = qo = qn;
qn->func = func;
qn->sense = sense;
qn->data = data;
qn->sdata = sdata;
qn->range = g_range;
qn->units = g_units;
qn->amc = g_amc;
qn = NULL;
qualct++;
}
そして先ほど見ていたwhileループのちょっと前にあるこの部分で呼び出し。
zsh/glob.c at 00d20ed15e18f5af682f0daec140d6b8383c479a · zsh-users/zsh
if ((!((qn->func) (news, bp, qn->data, qn->sdata))
^ qn->sense) & 1) {
/* Try next alternative, or return if there are no more */
if (!(qo = qo->or)) {
unqueue_signals();
return;
}
qn = qo;
continue;
}
qn = qn->next;
C言語のC23ではautoとlambdaの導入が検討されているみたいで、楽しみです。
— Yutaka Hirata (@yutakakn) November 5, 2021
しかし、開発の現場で導入するかは揉めそう。
年寄り「新しいものは保守できなくなる。そもそもチャラチャラしたのが嫌いじゃ!」
若者「開発効率上がるからいいんですよ!そうやって古いものにしがみつくから…」 pic.twitter.com/JmnDDP5MYt