From da02defb91c3a1bda0ea7e653d81f997f1c8884a Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 17 Nov 2022 16:02:57 -0500 Subject: expr: Don't use reftime for -ls reftime is part of a different union than the print actions are supposed to use. --- src/ctx.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/ctx.c') diff --git a/src/ctx.c b/src/ctx.c index b9d15bb..0403299 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -23,6 +23,7 @@ #include "pwcache.h" #include "stat.h" #include "trie.h" +#include "xtime.h" #include #include #include @@ -109,6 +110,10 @@ struct bfs_ctx *bfs_ctx_new(void) { goto fail; } + if (xgettime(&ctx->now) != 0) { + goto fail; + } + return ctx; fail: -- cgit v1.2.3 From 9463fdd30d392c98de7b5712d30dfbaeada40e99 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 25 Jan 2023 16:14:11 -0500 Subject: Replace license boilerplate with SPDX tags And while I'm at it, remove years from copyright declarations. Link: https://spdx.dev/about/ Link: https://daniel.haxx.se/blog/2023/01/08/copyright-without-years/ --- LICENSE | 33 +++++++++++++++++++++------------ Makefile | 17 ++--------------- completions/bfs.bash | 21 ++++----------------- src/bar.c | 17 ++--------------- src/bar.h | 17 ++--------------- src/bfstd.c | 17 ++--------------- src/bfstd.h | 17 ++--------------- src/bftw.c | 17 ++--------------- src/bftw.h | 17 ++--------------- src/color.c | 17 ++--------------- src/color.h | 17 ++--------------- src/config.h | 17 ++--------------- src/ctx.c | 17 ++--------------- src/ctx.h | 17 ++--------------- src/darray.c | 17 ++--------------- src/darray.h | 17 ++--------------- src/diag.c | 17 ++--------------- src/diag.h | 17 ++--------------- src/dir.c | 17 ++--------------- src/dir.h | 17 ++--------------- src/dstring.c | 17 ++--------------- src/dstring.h | 17 ++--------------- src/eval.c | 17 ++--------------- src/eval.h | 17 ++--------------- src/exec.c | 17 ++--------------- src/exec.h | 17 ++--------------- src/expr.h | 17 ++--------------- src/fsade.c | 17 ++--------------- src/fsade.h | 17 ++--------------- src/main.c | 17 ++--------------- src/mtab.c | 17 ++--------------- src/mtab.h | 17 ++--------------- src/opt.c | 17 ++--------------- src/opt.h | 17 ++--------------- src/parse.c | 17 ++--------------- src/parse.h | 17 ++--------------- src/printf.c | 17 ++--------------- src/printf.h | 17 ++--------------- src/pwcache.c | 17 ++--------------- src/pwcache.h | 17 ++--------------- src/stat.c | 17 ++--------------- src/stat.h | 17 ++--------------- src/trie.c | 17 ++--------------- src/trie.h | 17 ++--------------- src/typo.c | 17 ++--------------- src/typo.h | 17 ++--------------- src/xregex.c | 17 ++--------------- src/xregex.h | 18 ++---------------- src/xspawn.c | 17 ++--------------- src/xspawn.h | 17 ++--------------- src/xtime.c | 17 ++--------------- src/xtime.h | 17 ++--------------- tests/bfstd.c | 17 ++--------------- tests/find-color.sh | 17 ++--------------- tests/ls-color.sh | 17 ++--------------- tests/mksock.c | 17 ++--------------- tests/tests.sh | 17 ++--------------- tests/trie.c | 17 ++--------------- tests/xtimegm.c | 17 ++--------------- tests/xtouch.c | 17 ++--------------- 60 files changed, 141 insertions(+), 900 deletions(-) (limited to 'src/ctx.c') diff --git a/LICENSE b/LICENSE index 069b145..290e3d3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,12 +1,21 @@ -Copyright (C) 2015-2021 Tavian Barnes - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +Copyright © 2015-2023 Tavian Barnes and the bfs contributors + +Permission to use, copy, modify, and/or distribute this software for any purpose with or +without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT +SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE +USE OR PERFORMANCE OF THIS SOFTWARE. + +--- + +bfs is licensed under the Zero Clause BSD License. Individual files contain the following +tag instead of the full license text: + + SPDX-License-Identifier: 0BSD + +This enables machine processing of license information based on the SPDX License +Identifiers that are available here: https://spdx.org/licenses/ diff --git a/Makefile b/Makefile index b39a88a..0247b47 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,5 @@ -############################################################################ -# bfs # -# Copyright (C) 2015-2023 Tavian Barnes # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD ifneq ($(wildcard .git),) VERSION := $(shell git describe --always 2>/dev/null) diff --git a/completions/bfs.bash b/completions/bfs.bash index f734ab1..2f52e8d 100644 --- a/completions/bfs.bash +++ b/completions/bfs.bash @@ -1,21 +1,8 @@ -# bash completion script for bfs +# Copyright © Benjamin Mundt +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD -############################################################################ -# bfs # -# Copyright (C) 2020 Benjamin Mundt # -# Copyright (C) 2021 Tavian Barnes # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# bash completion script for bfs _bfs() { local cur prev words cword diff --git a/src/bar.c b/src/bar.c index 37d33c8..bd5d381 100644 --- a/src/bar.c +++ b/src/bar.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "bar.h" #include "bfstd.h" diff --git a/src/bar.h b/src/bar.h index 3e509d6..20d92a9 100644 --- a/src/bar.h +++ b/src/bar.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A terminal status bar. diff --git a/src/bfstd.c b/src/bfstd.c index 3a37250..437e9c9 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "bfstd.h" #include "config.h" diff --git a/src/bfstd.h b/src/bfstd.h index 79307cc..0e11b66 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Standard library wrappers and polyfills. diff --git a/src/bftw.c b/src/bftw.c index 5f3ebde..9feca79 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The bftw() implementation consists of the following components: diff --git a/src/bftw.h b/src/bftw.h index c458e1b..77697ed 100644 --- a/src/bftw.h +++ b/src/bftw.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2021 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A file-walking API based on nftw(). diff --git a/src/color.c b/src/color.c index 7c16ec5..589e631 100644 --- a/src/color.c +++ b/src/color.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "color.h" #include "bfstd.h" diff --git a/src/color.h b/src/color.h index 5b350cc..737c34b 100644 --- a/src/color.h +++ b/src/color.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2021 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Utilities for colored output on ANSI terminals. diff --git a/src/config.h b/src/config.h index 810f913..5ae9c82 100644 --- a/src/config.h +++ b/src/config.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Configuration and feature/platform detection. diff --git a/src/ctx.c b/src/ctx.c index 0403299..ff4a2a7 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "ctx.h" #include "color.h" diff --git a/src/ctx.h b/src/ctx.h index 6755d02..4c748b7 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * bfs execution context. diff --git a/src/darray.c b/src/darray.c index 6585d30..42b8397 100644 --- a/src/darray.c +++ b/src/darray.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "darray.h" #include diff --git a/src/darray.h b/src/darray.h index 4464381..cc6cc42 100644 --- a/src/darray.h +++ b/src/darray.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A dynamic array library. diff --git a/src/diag.c b/src/diag.c index b02473a..53db98e 100644 --- a/src/diag.c +++ b/src/diag.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "diag.h" #include "bfstd.h" diff --git a/src/diag.h b/src/diag.h index 8d0b19f..2952e30 100644 --- a/src/diag.h +++ b/src/diag.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Formatters for diagnostic messages. diff --git a/src/dir.c b/src/dir.c index 2081cc5..3d18eb2 100644 --- a/src/dir.c +++ b/src/dir.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2021-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "dir.h" #include "bfstd.h" diff --git a/src/dir.h b/src/dir.h index 69344c6..01eaaba 100644 --- a/src/dir.h +++ b/src/dir.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2021 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Directories and their contents. diff --git a/src/dstring.c b/src/dstring.c index f344d09..9112e54 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "dstring.h" #include diff --git a/src/dstring.h b/src/dstring.h index df20a04..ee3b345 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A dynamic string library. diff --git a/src/eval.c b/src/eval.c index d297af1..53ce605 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Implementation of all the primary expressions. diff --git a/src/eval.h b/src/eval.h index a50dc4e..3d70319 100644 --- a/src/eval.h +++ b/src/eval.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The evaluation functions that implement primary expressions like -name, diff --git a/src/exec.c b/src/exec.c index 8630469..6bde1c1 100644 --- a/src/exec.c +++ b/src/exec.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "exec.h" #include "bfstd.h" diff --git a/src/exec.h b/src/exec.h index a3e3c71..9d4192d 100644 --- a/src/exec.h +++ b/src/exec.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Implementation of -exec/-execdir/-ok/-okdir. diff --git a/src/expr.h b/src/expr.h index a52007a..1628cac 100644 --- a/src/expr.h +++ b/src/expr.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The expression tree representation. diff --git a/src/fsade.c b/src/fsade.c index 45969d1..a61a6b8 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2021 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "fsade.h" #include "config.h" diff --git a/src/fsade.h b/src/fsade.h index f45c6fd..9bef892 100644 --- a/src/fsade.h +++ b/src/fsade.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A facade over (file)system features that are (un)implemented differently diff --git a/src/main.c b/src/main.c index fbddbe5..4f99580 100644 --- a/src/main.c +++ b/src/main.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * - main(): the entry point for bfs(1), a breadth-first version of find(1) diff --git a/src/mtab.c b/src/mtab.c index 07d7a53..ae6dfd2 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "mtab.h" #include "bfstd.h" diff --git a/src/mtab.h b/src/mtab.h index 807539d..5dfdf6c 100644 --- a/src/mtab.h +++ b/src/mtab.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A facade over platform-specific APIs for enumerating mounted filesystems. diff --git a/src/opt.c b/src/opt.c index e76e216..5505b7b 100644 --- a/src/opt.c +++ b/src/opt.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The expression optimizer. Different optimization levels are supported: diff --git a/src/opt.h b/src/opt.h index 5f8180d..28cadb9 100644 --- a/src/opt.h +++ b/src/opt.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Optimization. diff --git a/src/parse.c b/src/parse.c index f2582e0..30bf56d 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The command line parser. Expressions are parsed by recursive descent, with a diff --git a/src/parse.h b/src/parse.h index 7e29a03..6895c9f 100644 --- a/src/parse.h +++ b/src/parse.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * bfs command line parsing. diff --git a/src/printf.c b/src/printf.c index 7c0c8db..1b4f2d4 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "printf.h" #include "bfstd.h" diff --git a/src/printf.h b/src/printf.h index a8c5f2a..2bff087 100644 --- a/src/printf.h +++ b/src/printf.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Implementation of -printf/-fprintf. diff --git a/src/pwcache.c b/src/pwcache.c index 868ec8f..5026dee 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "pwcache.h" #include "darray.h" diff --git a/src/pwcache.h b/src/pwcache.h index f1ca0bf..b6c0b67 100644 --- a/src/pwcache.h +++ b/src/pwcache.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A caching wrapper for /etc/{passwd,group}. diff --git a/src/stat.c b/src/stat.c index 94dedef..aaa5eac 100644 --- a/src/stat.c +++ b/src/stat.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2018-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "stat.h" #include "bfstd.h" diff --git a/src/stat.h b/src/stat.h index 44cbfad..7a70146 100644 --- a/src/stat.h +++ b/src/stat.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2018-2019 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A facade over the stat() API that unifies some details that diverge between diff --git a/src/trie.c b/src/trie.c index 08a99b5..77c43cc 100644 --- a/src/trie.c +++ b/src/trie.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * This is an implementation of a "qp trie," as documented at diff --git a/src/trie.h b/src/trie.h index 2ede6ea..03ee64d 100644 --- a/src/trie.h +++ b/src/trie.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #ifndef BFS_TRIE_H #define BFS_TRIE_H diff --git a/src/typo.c b/src/typo.c index c16cab4..305711d 100644 --- a/src/typo.c +++ b/src/typo.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "typo.h" #include diff --git a/src/typo.h b/src/typo.h index 0347aae..13eaa67 100644 --- a/src/typo.h +++ b/src/typo.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #ifndef BFS_TYPO_H #define BFS_TYPO_H diff --git a/src/xregex.c b/src/xregex.c index 6f0e5a1..5f5480f 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "xregex.h" #include "config.h" diff --git a/src/xregex.h b/src/xregex.h index b2f56a5..998a2b0 100644 --- a/src/xregex.h +++ b/src/xregex.h @@ -1,19 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 Tavian Barnes and bfs * - * contributors * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes and the bfs contributors +// SPDX-License-Identifier: 0BSD #ifndef BFS_XREGEX_H #define BFS_XREGEX_H diff --git a/src/xspawn.c b/src/xspawn.c index f76267b..a30c264 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2018-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "xspawn.h" #include "bfstd.h" diff --git a/src/xspawn.h b/src/xspawn.h index cd6a42e..3dbf5d2 100644 --- a/src/xspawn.h +++ b/src/xspawn.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2018-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A process-spawning library inspired by posix_spawn(). diff --git a/src/xtime.c b/src/xtime.c index 079d42a..82690d0 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "xtime.h" #include diff --git a/src/xtime.h b/src/xtime.h index b49cd04..75d1f4e 100644 --- a/src/xtime.h +++ b/src/xtime.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Date/time handling. diff --git a/tests/bfstd.c b/tests/bfstd.c index 4a8181b..8c61072 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "../src/bfstd.h" #include diff --git a/tests/find-color.sh b/tests/find-color.sh index ecdd5af..47de2a2 100755 --- a/tests/find-color.sh +++ b/tests/find-color.sh @@ -1,20 +1,7 @@ #!/usr/bin/env bash -############################################################################ -# bfs # -# Copyright (C) 2019 Tavian Barnes # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD set -e diff --git a/tests/ls-color.sh b/tests/ls-color.sh index c82a58d..6d33f53 100755 --- a/tests/ls-color.sh +++ b/tests/ls-color.sh @@ -1,20 +1,7 @@ #!/usr/bin/env bash -############################################################################ -# bfs # -# Copyright (C) 2019 Tavian Barnes # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD # Prints the "ground truth" coloring of a path using ls diff --git a/tests/mksock.c b/tests/mksock.c index 5068bc8..05edbeb 100644 --- a/tests/mksock.c +++ b/tests/mksock.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * There's no standard Unix utility that creates a socket file, so this small diff --git a/tests/tests.sh b/tests/tests.sh index e57db4e..98d332c 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -1,20 +1,7 @@ #!/usr/bin/env bash -############################################################################ -# bfs # -# Copyright (C) 2015-2022 Tavian Barnes # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD set -euP umask 022 diff --git a/tests/trie.c b/tests/trie.c index 88e92da..c2af18a 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #undef NDEBUG diff --git a/tests/xtimegm.c b/tests/xtimegm.c index d774b9e..bab64ba 100644 --- a/tests/xtimegm.c +++ b/tests/xtimegm.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "../src/xtime.h" #include diff --git a/tests/xtouch.c b/tests/xtouch.c index 9a91ec7..506c73d 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "../src/bfstd.h" #include "../src/xtime.h" -- cgit v1.2.3 From 88581b9d505342e20d03cc4c6557d30c3f66f0f5 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 18 May 2023 13:27:04 -0400 Subject: Use bfs_bug("...") over assert(!"...") --- src/bfstd.c | 3 ++- src/color.c | 3 ++- src/ctx.c | 3 +-- src/dstring.c | 4 ++-- src/eval.c | 6 +++--- src/parse.c | 4 ++-- src/stat.c | 6 +++--- 7 files changed, 15 insertions(+), 14 deletions(-) (limited to 'src/ctx.c') diff --git a/src/bfstd.c b/src/bfstd.c index 932f2c4..91383a2 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -3,6 +3,7 @@ #include "bfstd.h" #include "config.h" +#include "diag.h" #include "xregex.h" #include #include @@ -95,7 +96,7 @@ FILE *xfopen(const char *path, int flags) { strcpy(mode, "r+b"); break; default: - assert(!"Invalid access mode"); + bfs_bug("Invalid access mode"); errno = EINVAL; return NULL; } diff --git a/src/color.c b/src/color.c index eeadf98..43ea9a4 100644 --- a/src/color.c +++ b/src/color.c @@ -5,6 +5,7 @@ #include "bfstd.h" #include "bftw.h" #include "config.h" +#include "diag.h" #include "dir.h" #include "dstring.h" #include "expr.h" @@ -1085,7 +1086,7 @@ static int cvbuff(CFILE *cfile, const char *format, va_list args) { return 0; invalid: - assert(!"Invalid format string"); + bfs_bug("Invalid format string"); errno = EINVAL; return -1; } diff --git a/src/ctx.c b/src/ctx.c index ff4a2a7..c4b2fb2 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -11,7 +11,6 @@ #include "stat.h" #include "trie.h" #include "xtime.h" -#include #include #include #include @@ -38,7 +37,7 @@ const char *debug_flag_name(enum debug_flags flag) { break; } - assert(!"Unrecognized debug flag"); + bfs_bug("Unrecognized debug flag"); return "???"; } diff --git a/src/dstring.c b/src/dstring.c index 05efb10..2c9869d 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -2,7 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "dstring.h" -#include +#include "diag.h" #include #include #include @@ -184,7 +184,7 @@ int dstrvcatf(char **str, const char *format, va_list args) { tail = *str + len; ret = vsnprintf(tail, tail_len + 1, format, copy); if (ret < 0 || (size_t)ret != tail_len) { - assert(!"Length of formatted string changed"); + bfs_bug("Length of formatted string changed"); goto fail; } } diff --git a/src/eval.c b/src/eval.c index 7444ec9..6cad3a9 100644 --- a/src/eval.c +++ b/src/eval.c @@ -142,7 +142,7 @@ bool bfs_expr_cmp(const struct bfs_expr *expr, long long n) { return n > expr->num; } - assert(!"Invalid comparison mode"); + bfs_bug("Invalid comparison mode"); return false; } @@ -476,7 +476,7 @@ bool eval_flags(const struct bfs_expr *expr, struct bfs_eval *state) { return (flags & set) || (flags & clear) != clear; } - assert(!"Invalid comparison mode"); + bfs_bug("Invalid comparison mode"); return false; } @@ -626,7 +626,7 @@ bool eval_perm(const struct bfs_expr *expr, struct bfs_eval *state) { return !(mode & target) == !target; } - assert(!"Invalid comparison mode"); + bfs_bug("Invalid comparison mode"); return false; } diff --git a/src/parse.c b/src/parse.c index 55f1e74..807892c 100644 --- a/src/parse.c +++ b/src/parse.c @@ -638,7 +638,7 @@ static const char *parse_int(const struct parser_state *state, char **arg, const break; default: - assert(!"Invalid int size"); + bfs_bug("Invalid int size"); goto bad; } @@ -3492,7 +3492,7 @@ static const char *bftw_strategy_name(enum bftw_strategy strategy) { return "eds"; } - assert(!"Invalid strategy"); + bfs_bug("Invalid strategy"); return "???"; } diff --git a/src/stat.c b/src/stat.c index 50ea345..590a1d6 100644 --- a/src/stat.c +++ b/src/stat.c @@ -4,7 +4,7 @@ #include "stat.h" #include "bfstd.h" #include "config.h" -#include +#include "diag.h" #include #include #include @@ -57,7 +57,7 @@ const char *bfs_stat_field_name(enum bfs_stat_field field) { return "modification time"; } - assert(!"Unrecognized stat field"); + bfs_bug("Unrecognized stat field"); return "???"; } @@ -340,7 +340,7 @@ const struct timespec *bfs_stat_time(const struct bfs_stat *buf, enum bfs_stat_f case BFS_STAT_MTIME: return &buf->mtime; default: - assert(!"Invalid stat field for time"); + bfs_bug("Invalid stat field for time"); errno = EINVAL; return NULL; } -- cgit v1.2.3 From c023cceb3f50d92ed565ea3f085883f86de0f3f0 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 9 Jun 2023 16:34:41 -0400 Subject: bftw: Use an I/O queue to open directories Parallelism is controlled by the new -j flag. --- docs/bfs.1 | 8 +++- src/bftw.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/bftw.h | 2 + src/ctx.c | 1 + src/ctx.h | 2 + src/eval.c | 22 +++++++++ src/parse.c | 26 +++++++++- 7 files changed, 206 insertions(+), 9 deletions(-) (limited to 'src/ctx.c') diff --git a/docs/bfs.1 b/docs/bfs.1 index 53a9831..bc82457 100644 --- a/docs/bfs.1 +++ b/docs/bfs.1 @@ -171,12 +171,18 @@ consumes too much memory. .TP .I eds Exponential deepening search. -A compromise between breadth- and depth-first search, which searches exponentially increasing depth ranges (e.g 0-1, 1-2, 2-4, 4-8, etc.). +A compromise between breadth- and depth-first search, which searches exponentially increasing depth ranges (e.g. 0-1, 1-2, 2-4, 4-8, etc.). Provides many of the benefits of breadth-first search with depth-first's reduced memory consumption. Typically far faster than .B \-S .IR ids . .RE +.TP +\fB\-j\fIN\fR +Search with +.I N +threads in parallel (default: number of CPUs, up to +.IR 8 ). .SH OPERATORS .TP \fB( \fIexpression \fB)\fR diff --git a/src/bftw.c b/src/bftw.c index 4a49457..e711963 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -22,6 +22,7 @@ #include "diag.h" #include "dir.h" #include "dstring.h" +#include "ioq.h" #include "list.h" #include "mtab.h" #include "stat.h" @@ -58,6 +59,8 @@ struct bftw_file { size_t pincount; /** An open descriptor to this file, or -1. */ int fd; + /** Whether this file has a pending ioq request. */ + bool ioqueued; /** An open directory for this file, if any. */ struct bfs_dir *dir; @@ -264,6 +267,7 @@ static struct bftw_file *bftw_file_new(struct bftw_file *parent, const char *nam file->refcount = 1; file->pincount = 0; file->fd = -1; + file->ioqueued = false; file->dir = NULL; file->type = BFS_UNKNOWN; @@ -439,6 +443,8 @@ struct bftw_state { /** The cache of open directories. */ struct bftw_cache cache; + /** The async I/O queue. */ + struct ioq *ioq; /** The queue of directories to read. */ struct bftw_list dirs; /** The queue of files to visit. */ @@ -494,6 +500,25 @@ static int bftw_state_init(struct bftw_state *state, const struct bftw_args *arg bftw_cache_init(&state->cache, args->nopenfd); + size_t qdepth = args->nopenfd - 1; + if (qdepth > 1024) { + qdepth = 1024; + } + + size_t nthreads = args->nthreads; + if (nthreads > qdepth) { + nthreads = qdepth; + } + + state->ioq = NULL; + if (nthreads > 0) { + state->ioq = ioq_create(qdepth, nthreads); + if (!state->ioq) { + dstrfree(state->path); + return -1; + } + } + SLIST_INIT(&state->dirs); SLIST_INIT(&state->files); SLIST_INIT(&state->batch); @@ -846,15 +871,122 @@ static enum bftw_action bftw_call_back(struct bftw_state *state, const char *nam } } +/** Push a directory onto the queue. */ +static void bftw_push_dir(struct bftw_state *state, struct bftw_file *file) { + bfs_assert(file->type == BFS_DIR); + + struct bftw_cache *cache = &state->cache; + + if (!state->ioq) { + goto append; + } + + int dfd = AT_FDCWD; + if (file->parent) { + dfd = file->parent->fd; + if (dfd < 0) { + goto append; + } + bftw_cache_pin(cache, file->parent); + } + + if (cache->capacity == 0) { + if (bftw_cache_pop(cache) != 0) { + goto unpin; + } + } + --cache->capacity; + + if (ioq_opendir(state->ioq, dfd, file->name, file) != 0) { + ++cache->capacity; + goto unpin; + } + + file->ioqueued = true; + + if (state->flags & BFTW_SORT) { + goto append; + } else { + return; + } + +unpin: + if (file->parent) { + bftw_cache_unpin(cache, file->parent); + } +append: + SLIST_APPEND(&state->dirs, file); +} + +/** Pop a response from the I/O queue. */ +static int bftw_ioq_pop(struct bftw_state *state, bool block) { + if (!state->ioq) { + return -1; + } + + struct ioq_res *res; + if (block) { + res = ioq_pop(state->ioq); + } else { + res = ioq_trypop(state->ioq); + } + + if (!res) { + return -1; + } + + struct bftw_cache *cache = &state->cache; + ++cache->capacity; + + struct bftw_file *file = res->ptr; + file->ioqueued = false; + + if (file->parent) { + bftw_cache_unpin(cache, file->parent); + } + + if (res->dir) { + bftw_file_set_dir(cache, file, res->dir); + } + + ioq_free(state->ioq, res); + + if (!(state->flags & BFTW_SORT)) { + SLIST_PREPEND(&state->dirs, file); + } + + return 0; +} + /** Pop a directory to read from the queue. */ static bool bftw_pop_dir(struct bftw_state *state) { bfs_assert(!state->file); - if (state->files.head && state->strategy == BFTW_BFS) { + bool have_dirs = state->dirs.head; + bool have_files = state->files.head; + bool have_room = state->cache.capacity > 0; + + if (state->flags & BFTW_SORT) { + // Keep strict breadth-first order when sorting + if (state->strategy != BFTW_DFS && have_files) { + return false; + } + } else { + // Block if we have no other files/dirs to visit, or no room in the cache + bool block = !(have_dirs || have_files) || !have_room; + bftw_ioq_pop(state, block); + } + + struct bftw_file *dir = state->file = SLIST_POP(&state->dirs); + if (!dir) { return false; } - return (state->file = SLIST_POP(&state->dirs)); + while (dir->ioqueued) { + bftw_ioq_pop(state, true); + } + + return true; } /** Pop a file to visit from the queue. */ @@ -872,16 +1004,22 @@ static int bftw_opendir(struct bftw_state *state) { state->direrror = 0; - if (bftw_build_path(state, NULL) != 0) { - return -1; + struct bftw_file *file = state->file; + if (file->dir) { + state->dir = file->dir; + } else { + if (bftw_build_path(state, NULL) != 0) { + return -1; + } + state->dir = bftw_file_opendir(&state->cache, file, state->path); } - state->dir = bftw_file_opendir(&state->cache, state->file, state->path); if (state->dir) { - bftw_cache_pin(&state->cache, state->file); + bftw_cache_pin(&state->cache, file); } else { state->direrror = errno; } + return 0; } @@ -988,6 +1126,8 @@ static int bftw_state_destroy(struct bftw_state *state) { bftw_gc(state, BFTW_VISIT_NONE); } while (bftw_pop_dir(state) || bftw_pop_file(state)); + ioq_destroy(state->ioq); + bftw_cache_destroy(&state->cache); errno = state->error; @@ -1100,7 +1240,7 @@ static int bftw_visit(struct bftw_state *state, const char *name) { } bftw_save_ftwbuf(file, &state->ftwbuf); - SLIST_APPEND(&state->dirs, file); + bftw_push_dir(state, file); return 0; case BFTW_PRUNE: diff --git a/src/bftw.h b/src/bftw.h index 77697ed..940532c 100644 --- a/src/bftw.h +++ b/src/bftw.h @@ -186,6 +186,8 @@ struct bftw_args { void *ptr; /** The maximum number of file descriptors to keep open. */ int nopenfd; + /** The maximum number of threads to use. */ + int nthreads; /** Flags that control bftw() behaviour. */ enum bftw_flags flags; /** The search strategy to use. */ diff --git a/src/ctx.c b/src/ctx.c index c4b2fb2..e8ce0e8 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -56,6 +56,7 @@ struct bfs_ctx *bfs_ctx_new(void) { ctx->maxdepth = INT_MAX; ctx->flags = BFTW_RECOVER; ctx->strategy = BFTW_BFS; + ctx->threads = 0; ctx->optlevel = 3; ctx->debug = 0; ctx->ignore_races = false; diff --git a/src/ctx.h b/src/ctx.h index 0dc9f08..2b8e8cb 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -68,6 +68,8 @@ struct bfs_ctx { /** bftw() search strategy. */ enum bftw_strategy strategy; + /** Threads (-j). */ + int threads; /** Optimization level (-O). */ int optlevel; /** Debugging flags (-D). */ diff --git a/src/eval.c b/src/eval.c index e2c19a9..b9bce6c 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1505,6 +1505,19 @@ static int infer_fdlimit(const struct bfs_ctx *ctx, int limit) { return ret; } +static int infer_nproc(void) { + long nproc = sysconf(_SC_NPROCESSORS_ONLN); + + if (nproc < 0) { + nproc = 0; + } else if (nproc > 8) { + // Not much speedup after 8 threads + nproc = 8; + } + + return nproc; +} + /** * Dump the bftw() flags for -D search. */ @@ -1593,12 +1606,20 @@ int bfs_eval(const struct bfs_ctx *ctx) { int fdlimit = raise_fdlimit(ctx); fdlimit = infer_fdlimit(ctx, fdlimit); + int nthreads; + if (ctx->threads > 0) { + nthreads = ctx->threads - 1; + } else { + nthreads = infer_nproc(); + } + struct bftw_args bftw_args = { .paths = ctx->paths, .npaths = darray_length(ctx->paths), .callback = eval_callback, .ptr = &args, .nopenfd = fdlimit, + .nthreads = nthreads, .flags = ctx->flags, .strategy = ctx->strategy, .mtab = bfs_ctx_mtab(ctx), @@ -1618,6 +1639,7 @@ int bfs_eval(const struct bfs_ctx *ctx) { fprintf(stderr, "\t.callback = eval_callback,\n"); fprintf(stderr, "\t.ptr = &args,\n"); fprintf(stderr, "\t.nopenfd = %d,\n", bftw_args.nopenfd); + fprintf(stderr, "\t.nthreads = %d,\n", bftw_args.nthreads); fprintf(stderr, "\t.flags = "); dump_bftw_flags(bftw_args.flags); fprintf(stderr, ",\n\t.strategy = %s,\n", dump_bftw_strategy(bftw_args.strategy)); diff --git a/src/parse.c b/src/parse.c index 59a1e7d..96def14 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1636,6 +1636,23 @@ static struct bfs_expr *parse_inum(struct parser_state *state, int arg1, int arg return parse_test_icmp(state, eval_inum); } +/** + * Parse -j. + */ +static struct bfs_expr *parse_jobs(struct parser_state *state, int arg1, int arg2) { + struct bfs_expr *expr = parse_nullary_flag(state); + if (!expr) { + return NULL; + } + + if (!parse_int(state, expr->argv, expr->argv[0] + 2, &state->ctx->threads, IF_INT | IF_UNSIGNED)) { + bfs_expr_free(expr); + return NULL; + } + + return expr; +} + /** * Parse -links N. */ @@ -2753,7 +2770,9 @@ static struct bfs_expr *parse_help(struct parser_state *state, int arg1, int arg cfprintf(cout, " Enable optimization level ${bld}N${rs} (default: ${bld}3${rs})\n"); cfprintf(cout, " ${cyn}-S${rs} ${bld}bfs${rs}|${bld}dfs${rs}|${bld}ids${rs}|${bld}eds${rs}\n"); cfprintf(cout, " Use ${bld}b${rs}readth-${bld}f${rs}irst/${bld}d${rs}epth-${bld}f${rs}irst/${bld}i${rs}terative/${bld}e${rs}xponential ${bld}d${rs}eepening ${bld}s${rs}earch\n"); - cfprintf(cout, " (default: ${cyn}-S${rs} ${bld}bfs${rs})\n\n"); + cfprintf(cout, " (default: ${cyn}-S${rs} ${bld}bfs${rs})\n"); + cfprintf(cout, " ${cyn}-j${bld}N${rs}\n"); + cfprintf(cout, " Search with ${bld}N${rs} threads in parallel (default: number of CPUs, up to ${bld}8${rs})\n\n"); cfprintf(cout, "${bld}Operators:${rs}\n\n"); @@ -3060,6 +3079,7 @@ static const struct table_entry parse_table[] = { {"-ipath", T_TEST, parse_path, true}, {"-iregex", T_TEST, parse_regex, BFS_REGEX_ICASE}, {"-iwholename", T_TEST, parse_path, true}, + {"-j", T_FLAG, parse_jobs, 0, 0, true}, {"-links", T_TEST, parse_links}, {"-lname", T_TEST, parse_lname, false}, {"-ls", T_ACTION, parse_ls}, @@ -3552,6 +3572,10 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) { cfprintf(cerr, " ${cyn}-O${bld}%d${rs}", ctx->optlevel); } + if (ctx->threads > 0) { + cfprintf(cerr, " ${cyn}-j${bld}%d${rs}", ctx->threads); + } + cfprintf(cerr, " ${cyn}-S${rs} ${bld}%s${rs}", bftw_strategy_name(ctx->strategy)); enum debug_flags debug = ctx->debug; -- cgit v1.2.3 From 90ded13e589b0089167ef25ca3d26be599dfec9b Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 19 Jun 2023 12:11:36 -0400 Subject: alloc: New header for memory allocation utilities --- Makefile | 3 +- src/alloc.c | 50 ++++++++++++++++++++ src/alloc.h | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/bfstd.c | 13 ----- src/bfstd.h | 12 ----- src/bftw.c | 5 +- src/color.c | 5 +- src/config.h | 50 -------------------- src/ctx.c | 31 ++---------- src/dstring.c | 3 +- src/exec.c | 26 ++++------ src/ioq.c | 26 ++++------ src/ioq.h | 4 +- src/main.c | 1 + src/mtab.c | 5 +- src/parse.c | 28 ++--------- src/pwcache.c | 5 +- src/trie.c | 7 ++- src/xregex.c | 3 +- src/xspawn.c | 3 +- tests/alloc.c | 24 ++++++++++ tests/bfstd.c | 13 +---- 22 files changed, 271 insertions(+), 195 deletions(-) create mode 100644 src/alloc.c create mode 100644 src/alloc.h create mode 100644 tests/alloc.c (limited to 'src/ctx.c') diff --git a/Makefile b/Makefile index fb28e29..d38f581 100644 --- a/Makefile +++ b/Makefile @@ -217,6 +217,7 @@ $(OBJ)/FLAGS: $(OBJ)/FLAGS.new # All object files except the entry point LIBBFS := \ + $(OBJ)/src/alloc.o \ $(OBJ)/src/bar.o \ $(OBJ)/src/bfstd.o \ $(OBJ)/src/bftw.o \ @@ -246,7 +247,7 @@ LIBBFS := \ $(BIN)/bfs: $(OBJ)/src/main.o $(LIBBFS) # Standalone unit tests -UNITS := bfstd bit trie xtimegm +UNITS := alloc bfstd bit trie xtimegm UNIT_TESTS := $(UNITS:%=$(BIN)/tests/%) UNIT_CHECKS := $(UNITS:%=check-%) diff --git a/src/alloc.c b/src/alloc.c new file mode 100644 index 0000000..0003108 --- /dev/null +++ b/src/alloc.c @@ -0,0 +1,50 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include "alloc.h" +#include "bit.h" +#include "diag.h" +#include +#include +#include + +/** Portable aligned_alloc()/posix_memalign(). */ +static void *xmemalign(size_t align, size_t size) { + bfs_assert(has_single_bit(align)); + bfs_assert(align >= sizeof(void *)); + bfs_assert((size & (align - 1)) == 0); + +#if __APPLE__ + void *ptr = NULL; + errno = posix_memalign(&ptr, align, size); + return ptr; +#else + return aligned_alloc(align, size); +#endif +} + +void *alloc(size_t align, size_t size) { + bfs_assert(has_single_bit(align)); + bfs_assert((size & (align - 1)) == 0); + + if (align <= alignof(max_align_t)) { + return malloc(size); + } else { + return xmemalign(align, size); + } +} + +void *zalloc(size_t align, size_t size) { + bfs_assert(has_single_bit(align)); + bfs_assert((size & (align - 1)) == 0); + + if (align <= alignof(max_align_t)) { + return calloc(1, size); + } + + void *ret = xmemalign(align, size); + if (ret) { + memset(ret, 0, size); + } + return ret; +} diff --git a/src/alloc.h b/src/alloc.h new file mode 100644 index 0000000..899a4ec --- /dev/null +++ b/src/alloc.h @@ -0,0 +1,149 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +/** + * Memory allocation. + */ + +#ifndef BFS_ALLOC_H +#define BFS_ALLOC_H + +#include "config.h" +#include + +/** Round down to a multiple of an alignment. */ +static inline size_t align_floor(size_t align, size_t size) { + return size & ~(align - 1); +} + +/** Round up to a multiple of an alignment. */ +static inline size_t align_ceil(size_t align, size_t size) { + return align_floor(align, size + align - 1); +} + +/** + * Saturating array size. + * + * @param align + * Array element alignment. + * @param size + * Array element size. + * @param count + * Array element count. + * @return + * size * count, saturating to the maximum aligned value on overflow. + */ +static inline size_t array_size(size_t align, size_t size, size_t count) { + size_t ret = size * count; + return ret / size == count ? ret : ~(align - 1); +} + +/** Saturating array sizeof. */ +#define sizeof_array(type, count) \ + array_size(alignof(type), sizeof(type), count) + +/** Size of a struct/union field. */ +#define sizeof_member(type, member) \ + sizeof(((type *)NULL)->member) + +/** + * Saturating flexible struct size. + * + * @param align + * Struct alignment. + * @param min + * Minimum struct size. + * @param offset + * Flexible array member offset. + * @param size + * Flexible array element size. + * @param count + * Flexible array element count. + * @return + * The size of the struct with count flexible array elements. Saturates + * to the maximum aligned value on overflow. + */ +static inline size_t flex_size(size_t align, size_t min, size_t offset, size_t size, size_t count) { + size_t ret = size * count; + size_t overflow = ret / size != count; + + size_t extra = offset + align - 1; + ret += extra; + overflow |= ret < extra; + ret |= -overflow; + ret = align_floor(align, ret); + + // Make sure flex_sizeof(type, member, 0) >= sizeof(type), even if the + // type has more padding than necessary for alignment + if (min > align_ceil(align, offset)) { + ret = ret < min ? min : ret; + } + + return ret; +} + +/** + * Computes the size of a flexible struct. + * + * @param type + * The type of the struct containing the flexible array. + * @param member + * The name of the flexible array member. + * @param count + * The length of the flexible array. + * @return + * The size of the struct with count flexible array elements. Saturates + * to the maximum aligned value on overflow. + */ +#define sizeof_flex(type, member, count) \ + flex_size(alignof(type), sizeof(type), offsetof(type, member), sizeof_member(type, member[0]), count) + +/** + * General memory allocator. + * + * @param align + * The required alignment. + * @param size + * The size of the allocation. + * @return + * The allocated memory, or NULL on failure. + */ +void *alloc(size_t align, size_t size); + +/** + * Zero-initialized memory allocator. + * + * @param align + * The required alignment. + * @param size + * The size of the allocation. + * @return + * The allocated memory, or NULL on failure. + */ +void *zalloc(size_t align, size_t size); + +/** Allocate memory for the given type. */ +#define ALLOC(type) \ + (type *)alloc(alignof(type), sizeof(type)) + +/** Allocate zeroed memory for the given type. */ +#define ZALLOC(type) \ + (type *)zalloc(alignof(type), sizeof(type)) + +/** Allocate memory for an array. */ +#define ALLOC_ARRAY(type, count) \ + (type *)alloc(alignof(type), sizeof_array(type, count)); + +/** Allocate zeroed memory for an array. */ +#define ZALLOC_ARRAY(type, count) \ + (type *)zalloc(alignof(type), sizeof_array(type, count)); + +/** Allocate memory for a flexible struct. */ +#define ALLOC_FLEX(type, member, count) \ + (type *)alloc(alignof(type), sizeof_flex(type, member, count)) + +/** Allocate zeroed memory for a flexible struct. */ +#define ZALLOC_FLEX(type, member, count) \ + (type *)zalloc(alignof(type), sizeof_flex(type, member, count)) + +#endif // BFS_ALLOC_H diff --git a/src/bfstd.c b/src/bfstd.c index 856c76c..0e8ba5f 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -143,19 +143,6 @@ char *xgetdelim(FILE *file, char delim) { } } -void *xmemalign(size_t align, size_t size) { - bfs_assert(has_single_bit(align)); - bfs_assert((size & (align - 1)) == 0); - -#if __APPLE__ - void *ptr = NULL; - errno = posix_memalign(&ptr, align, size); - return ptr; -#else - return aligned_alloc(align, size); -#endif -} - /** Compile and execute a regular expression for xrpmatch(). */ static int xrpregex(nl_item item, const char *response) { const char *pattern = nl_langinfo(item); diff --git a/src/bfstd.h b/src/bfstd.h index 6f2e21e..cafe28f 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -105,18 +105,6 @@ char *xgetdelim(FILE *file, char delim); // #include -/** - * Portable version of aligned_alloc()/posix_memalign(). - * - * @param align - * The allocation's alignment. - * @param size - * The allocation's size. - * @return - * The allocation, or NULL on failure. - */ -void *xmemalign(size_t align, size_t size); - /** * Process a yes/no prompt. * diff --git a/src/bftw.c b/src/bftw.c index e711963..7ab14c7 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -17,6 +17,7 @@ */ #include "bftw.h" +#include "alloc.h" #include "bfstd.h" #include "config.h" #include "diag.h" @@ -241,9 +242,7 @@ static void bftw_cache_destroy(struct bftw_cache *cache) { /** Create a new bftw_file. */ static struct bftw_file *bftw_file_new(struct bftw_file *parent, const char *name) { size_t namelen = strlen(name); - size_t size = flex_sizeof(struct bftw_file, name, namelen + 1); - - struct bftw_file *file = malloc(size); + struct bftw_file *file = ALLOC_FLEX(struct bftw_file, name, namelen + 1); if (!file) { return NULL; } diff --git a/src/color.c b/src/color.c index 1edd8b5..b54ad53 100644 --- a/src/color.c +++ b/src/color.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "color.h" +#include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "config.h" @@ -404,7 +405,7 @@ static void parse_gnu_ls_colors(struct colors *colors, const char *ls_colors) { } struct colors *parse_colors(void) { - struct colors *colors = malloc(sizeof(struct colors)); + struct colors *colors = ALLOC(struct colors); if (!colors) { return NULL; } @@ -497,7 +498,7 @@ void free_colors(struct colors *colors) { } CFILE *cfwrap(FILE *file, const struct colors *colors, bool close) { - CFILE *cfile = malloc(sizeof(*cfile)); + CFILE *cfile = ALLOC(CFILE); if (!cfile) { return NULL; } diff --git a/src/config.h b/src/config.h index 73348ac..1671a0d 100644 --- a/src/config.h +++ b/src/config.h @@ -141,56 +141,6 @@ */ #define countof(array) (sizeof(array) / sizeof(0[array])) -/** - * Round down to a multiple of an alignment. - */ -static inline size_t align_floor(size_t align, size_t size) { - return size & ~(align - 1); -} - -/** - * Round up to a multiple of an alignment. - */ -static inline size_t align_ceil(size_t align, size_t size) { - return align_floor(align, size + align - 1); -} - -/** - * Computes the size of a struct containing a flexible array member of the given - * length. - * - * @param type - * The type of the struct containing the flexible array. - * @param member - * The name of the flexible array member. - * @param count - * The length of the flexible array. - */ -#define flex_sizeof(type, member, count) \ - flex_sizeof_impl(alignof(type), sizeof(type), offsetof(type, member), sizeof(((type *)NULL)->member[0]), count) - -static inline size_t flex_sizeof_impl(size_t align, size_t min, size_t offset, size_t size, size_t count) { - size_t ret = size * count; - size_t overflow = ret / size != count; - - ret += offset; - overflow |= ret < offset; - - size_t mask = align - 1; - ret += mask; - overflow |= ret < mask; - ret |= -overflow; - ret &= ~mask; - - // Make sure flex_sizeof(type, member, 0) >= sizeof(type), even if the - // type has more padding than necessary for alignment - if (min > align_ceil(align, offset) && ret < min) { - ret = min; - } - - return ret; -} - /** * False sharing/destructive interference/largest cache line size. */ diff --git a/src/ctx.c b/src/ctx.c index e8ce0e8..a940bed 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "ctx.h" +#include "alloc.h" #include "color.h" #include "darray.h" #include "diag.h" @@ -42,43 +43,17 @@ const char *debug_flag_name(enum debug_flags flag) { } struct bfs_ctx *bfs_ctx_new(void) { - struct bfs_ctx *ctx = malloc(sizeof(*ctx)); + struct bfs_ctx *ctx = ZALLOC(struct bfs_ctx); if (!ctx) { return NULL; } - ctx->argv = NULL; - ctx->paths = NULL; - ctx->expr = NULL; - ctx->exclude = NULL; - - ctx->mindepth = 0; ctx->maxdepth = INT_MAX; ctx->flags = BFTW_RECOVER; ctx->strategy = BFTW_BFS; - ctx->threads = 0; ctx->optlevel = 3; - ctx->debug = 0; - ctx->ignore_races = false; - ctx->posixly_correct = false; - ctx->status = false; - ctx->unique = false; - ctx->warn = false; - ctx->xargs_safe = false; - - ctx->colors = NULL; - ctx->colors_error = 0; - ctx->cout = NULL; - ctx->cerr = NULL; - - ctx->users = NULL; - ctx->groups = NULL; - - ctx->mtab = NULL; - ctx->mtab_error = 0; trie_init(&ctx->files); - ctx->nfiles = 0; struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) != 0) { @@ -155,7 +130,7 @@ CFILE *bfs_ctx_dedup(struct bfs_ctx *ctx, CFILE *cfile, const char *path) { return ctx_file->cfile; } - leaf->value = ctx_file = malloc(sizeof(*ctx_file)); + leaf->value = ctx_file = ALLOC(struct bfs_ctx_file); if (!ctx_file) { trie_remove(&ctx->files, leaf); return NULL; diff --git a/src/dstring.c b/src/dstring.c index 2c9869d..7ca74d0 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "dstring.h" +#include "alloc.h" #include "diag.h" #include #include @@ -24,7 +25,7 @@ static struct dstring *dstrheader(const char *dstr) { /** Get the correct size for a dstring with the given capacity. */ static size_t dstrsize(size_t capacity) { - return flex_sizeof(struct dstring, data, capacity + 1); + return sizeof_flex(struct dstring, data, capacity + 1); } /** Allocate a dstring with the given contents. */ diff --git a/src/exec.c b/src/exec.c index 5912ad6..ea7f897 100644 --- a/src/exec.c +++ b/src/exec.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "exec.h" +#include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "ctx.h" @@ -124,26 +125,16 @@ static void bfs_exec_parse_error(const struct bfs_ctx *ctx, const struct bfs_exe } struct bfs_exec *bfs_exec_parse(const struct bfs_ctx *ctx, char **argv, enum bfs_exec_flags flags) { - struct bfs_exec *execbuf = malloc(sizeof(*execbuf)); + struct bfs_exec *execbuf = ZALLOC(struct bfs_exec); if (!execbuf) { - bfs_perror(ctx, "malloc()"); + bfs_perror(ctx, "zalloc()"); goto fail; } execbuf->flags = flags; execbuf->ctx = ctx; execbuf->tmpl_argv = argv + 1; - execbuf->tmpl_argc = 0; - execbuf->argv = NULL; - execbuf->argc = 0; - execbuf->argv_cap = 0; - execbuf->arg_size = 0; - execbuf->arg_max = 0; - execbuf->arg_min = 0; execbuf->wd_fd = -1; - execbuf->wd_path = NULL; - execbuf->wd_len = 0; - execbuf->ret = 0; while (true) { const char *arg = execbuf->tmpl_argv[execbuf->tmpl_argc]; @@ -176,9 +167,9 @@ struct bfs_exec *bfs_exec_parse(const struct bfs_ctx *ctx, char **argv, enum bfs } execbuf->argv_cap = execbuf->tmpl_argc + 1; - execbuf->argv = malloc(execbuf->argv_cap*sizeof(*execbuf->argv)); + execbuf->argv = ALLOC_ARRAY(char *, execbuf->argv_cap); if (!execbuf->argv) { - bfs_perror(ctx, "malloc()"); + bfs_perror(ctx, "alloc()"); goto fail; } @@ -224,9 +215,8 @@ static char *bfs_exec_format_path(const struct bfs_exec *execbuf, const struct B return NULL; } - strcpy(path, "./"); - strcpy(path + 2, name); - + char *cur = stpcpy(path, "./"); + cur = stpcpy(cur, name); return path; } @@ -612,7 +602,7 @@ static int bfs_exec_push(struct bfs_exec *execbuf, char *arg) { if (execbuf->argc + 1 >= execbuf->argv_cap) { size_t cap = 2*execbuf->argv_cap; - char **argv = realloc(execbuf->argv, cap*sizeof(*argv)); + char **argv = realloc(execbuf->argv, sizeof_array(char *, cap)); if (!argv) { return -1; } diff --git a/src/ioq.c b/src/ioq.c index 5550c91..3e304ce 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "ioq.h" +#include "alloc.h" #include "atomic.h" #include "bfstd.h" #include "bit.h" @@ -114,7 +115,7 @@ static struct ioqq *ioqq_create(size_t size) { // Circular buffer size must be a power of two size = bit_ceil(size); - struct ioqq *ioqq = xmemalign(alignof(struct ioqq), flex_sizeof(struct ioqq, slots, size)); + struct ioqq *ioqq = ALLOC_FLEX(struct ioqq, slots, size); if (!ioqq) { return NULL; } @@ -124,7 +125,7 @@ static struct ioqq *ioqq_create(size_t size) { // Use a pool of monitors size_t nmonitors = size < 64 ? size : 64; - ioqq->monitors = xmemalign(alignof(struct ioq_monitor), nmonitors * sizeof(struct ioq_monitor)); + ioqq->monitors = ALLOC_ARRAY(struct ioq_monitor, nmonitors); if (!ioqq->monitors) { ioqq_destroy(ioqq); return NULL; @@ -273,7 +274,7 @@ struct ioq { /** The number of background threads. */ size_t nthreads; /** The background threads themselves. */ - pthread_t *threads; + pthread_t threads[]; }; /** Background thread entry point. */ @@ -303,18 +304,13 @@ static void *ioq_work(void *ptr) { return NULL; } -struct ioq *ioq_create(size_t depth, size_t threads) { - struct ioq *ioq = malloc(sizeof(*ioq)); +struct ioq *ioq_create(size_t depth, size_t nthreads) { + struct ioq *ioq = ZALLOC_FLEX(struct ioq, threads, nthreads); if (!ioq) { goto fail; } ioq->depth = depth; - ioq->size = 0; - - ioq->pending = NULL; - ioq->ready = NULL; - ioq->nthreads = 0; ioq->pending = ioqq_create(depth); if (!ioq->pending) { @@ -326,12 +322,7 @@ struct ioq *ioq_create(size_t depth, size_t threads) { goto fail; } - ioq->threads = malloc(threads * sizeof(ioq->threads[0])); - if (!ioq->threads) { - goto fail; - } - - for (size_t i = 0; i < threads; ++i) { + for (size_t i = 0; i < nthreads; ++i) { errno = pthread_create(&ioq->threads[i], NULL, ioq_work, ioq); if (errno != 0) { goto fail; @@ -354,7 +345,7 @@ int ioq_opendir(struct ioq *ioq, int dfd, const char *path, void *ptr) { return -1; } - union ioq_cmd *cmd = malloc(sizeof(*cmd)); + union ioq_cmd *cmd = ALLOC(union ioq_cmd); if (!cmd) { return -1; } @@ -412,7 +403,6 @@ void ioq_destroy(struct ioq *ioq) { abort(); } } - free(ioq->threads); ioqq_destroy(ioq->ready); ioqq_destroy(ioq->pending); diff --git a/src/ioq.h b/src/ioq.h index 9492034..0af5779 100644 --- a/src/ioq.h +++ b/src/ioq.h @@ -33,12 +33,12 @@ struct ioq_res { * * @param depth * The maximum depth of the queue. - * @param threads + * @param nthreads * The maximum number of background threads. * @return * The new I/O queue, or NULL on failure. */ -struct ioq *ioq_create(size_t depth, size_t threads); +struct ioq *ioq_create(size_t depth, size_t nthreads); /** * Asynchronous bfs_opendir(). diff --git a/src/main.c b/src/main.c index 76dde86..b7a08c1 100644 --- a/src/main.c +++ b/src/main.c @@ -20,6 +20,7 @@ * - bftw.[ch] (an extended version of nftw(3)) * * - Utilities: + * - alloc.[ch] (memory allocation) * - atomic.h (atomic operations) * - bar.[ch] (a terminal status bar) * - bit.h (bit manipulation) diff --git a/src/mtab.c b/src/mtab.c index 1d1ad94..e5c25ba 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "mtab.h" +#include "alloc.h" #include "bfstd.h" #include "config.h" #include "darray.h" @@ -87,15 +88,13 @@ fail: } struct bfs_mtab *bfs_mtab_parse(void) { - struct bfs_mtab *mtab = malloc(sizeof(*mtab)); + struct bfs_mtab *mtab = ZALLOC(struct bfs_mtab); if (!mtab) { return NULL; } - mtab->entries = NULL; trie_init(&mtab->names); trie_init(&mtab->types); - mtab->types_filled = false; int error = 0; diff --git a/src/parse.c b/src/parse.c index 64e08cd..cf4f696 100644 --- a/src/parse.c +++ b/src/parse.c @@ -9,6 +9,7 @@ */ #include "parse.h" +#include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" @@ -55,39 +56,16 @@ static char *fake_print_arg = "-print"; static char *fake_true_arg = "-true"; struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { - struct bfs_expr *expr = malloc(sizeof(*expr)); + struct bfs_expr *expr = ZALLOC(struct bfs_expr); if (!expr) { - perror("malloc()"); + perror("zalloc()"); return NULL; } expr->eval_fn = eval_fn; expr->argc = argc; expr->argv = argv; - expr->persistent_fds = 0; - expr->ephemeral_fds = 0; - expr->pure = false; - expr->always_true = false; - expr->always_false = false; - expr->cost = 0.0; expr->probability = 0.5; - expr->evaluations = 0; - expr->successes = 0; - expr->elapsed.tv_sec = 0; - expr->elapsed.tv_nsec = 0; - - // Prevent bfs_expr_free() from freeing uninitialized pointers on error paths - if (bfs_expr_is_parent(expr)) { - expr->lhs = NULL; - expr->rhs = NULL; - } else if (eval_fn == eval_exec) { - expr->exec = NULL; - } else if (eval_fn == eval_fprintf) { - expr->printf = NULL; - } else if (eval_fn == eval_regex) { - expr->regex = NULL; - } - return expr; } diff --git a/src/pwcache.c b/src/pwcache.c index f52e4e1..9f32eb0 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "pwcache.h" +#include "alloc.h" #include "config.h" #include "darray.h" #include "trie.h" @@ -71,7 +72,7 @@ struct bfs_users { }; struct bfs_users *bfs_users_new(void) { - struct bfs_users *users = malloc(sizeof(*users)); + struct bfs_users *users = ALLOC(struct bfs_users); if (!users) { return NULL; } @@ -144,7 +145,7 @@ struct bfs_groups { }; struct bfs_groups *bfs_groups_new(void) { - struct bfs_groups *groups = malloc(sizeof(*groups)); + struct bfs_groups *groups = ALLOC(struct bfs_groups); if (!groups) { return NULL; } diff --git a/src/trie.c b/src/trie.c index 8543eb1..19423cf 100644 --- a/src/trie.c +++ b/src/trie.c @@ -82,6 +82,7 @@ */ #include "trie.h" +#include "alloc.h" #include "bit.h" #include "config.h" #include "diag.h" @@ -317,7 +318,7 @@ struct trie_leaf *trie_find_prefix(const struct trie *trie, const char *key) { /** Create a new leaf, holding a copy of the given key. */ static struct trie_leaf *trie_leaf_alloc(struct trie *trie, const void *key, size_t length) { - struct trie_leaf *leaf = malloc(flex_sizeof(struct trie_leaf, key, length)); + struct trie_leaf *leaf = ALLOC_FLEX(struct trie_leaf, key, length); if (!leaf) { return NULL; } @@ -339,12 +340,10 @@ static void trie_leaf_free(struct trie *trie, struct trie_leaf *leaf) { /** Compute the size of a trie node with a certain number of children. */ static size_t trie_node_size(unsigned int size) { - // Empty nodes aren't supported - bfs_assert(size > 0); // Node size must be a power of two bfs_assert(has_single_bit(size)); - return flex_sizeof(struct trie_node, children, size); + return sizeof_flex(struct trie_node, children, size); } #if ENDIAN_NATIVE == ENDIAN_LITTLE diff --git a/src/xregex.c b/src/xregex.c index 89c2e90..ab5f793 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "xregex.h" +#include "alloc.h" #include "config.h" #include "diag.h" #include "sanity.h" @@ -115,7 +116,7 @@ static int bfs_onig_initialize(OnigEncoding *enc) { #endif int bfs_regcomp(struct bfs_regex **preg, const char *pattern, enum bfs_regex_type type, enum bfs_regcomp_flags flags) { - struct bfs_regex *regex = *preg = malloc(sizeof(*regex)); + struct bfs_regex *regex = *preg = ALLOC(struct bfs_regex); if (!regex) { return -1; } diff --git a/src/xspawn.c b/src/xspawn.c index 740e38e..2cabdcc 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "xspawn.h" +#include "alloc.h" #include "bfstd.h" #include "config.h" #include "list.h" @@ -62,7 +63,7 @@ int bfs_spawn_setflags(struct bfs_spawn *ctx, enum bfs_spawn_flags flags) { /** Add a spawn action to the chain. */ static struct bfs_spawn_action *bfs_spawn_add(struct bfs_spawn *ctx, enum bfs_spawn_op op) { - struct bfs_spawn_action *action = malloc(sizeof(*action)); + struct bfs_spawn_action *action = ALLOC(struct bfs_spawn_action); if (!action) { return NULL; } diff --git a/tests/alloc.c b/tests/alloc.c new file mode 100644 index 0000000..91b1b43 --- /dev/null +++ b/tests/alloc.c @@ -0,0 +1,24 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include "../src/alloc.h" +#include "../src/diag.h" +#include + +int main(void) { + // Check sizeof_flex() + struct flexible { + alignas(64) int foo; + int bar[]; + }; + bfs_verify(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible)); + bfs_verify(sizeof_flex(struct flexible, bar, 16) % alignof(struct flexible) == 0); + bfs_verify(sizeof_flex(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) + == align_floor(alignof(struct flexible), SIZE_MAX)); + + // Corner case: sizeof(type) > align_ceil(alignof(type), offsetof(type, member)) + // Doesn't happen in typical ABIs + bfs_verify(flex_size(8, 16, 4, 4, 1) == 16); + + return EXIT_SUCCESS; +} diff --git a/tests/bfstd.c b/tests/bfstd.c index 7fea9b5..fa854a8 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -24,17 +24,6 @@ static void check_base_dir(const char *path, const char *dir, const char *base) } int main(void) { - // Check flex_sizeof() - struct flexible { - alignas(64) int foo; - int bar[]; - }; - bfs_verify(flex_sizeof(struct flexible, bar, 0) >= sizeof(struct flexible)); - bfs_verify(flex_sizeof(struct flexible, bar, 16) % alignof(struct flexible) == 0); - bfs_verify(flex_sizeof(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) - == align_floor(alignof(struct flexible), SIZE_MAX)); - bfs_verify(flex_sizeof_impl(8, 16, 4, 4, 1) == 16); - // From man 3p basename check_base_dir("usr", ".", "usr"); check_base_dir("usr/", ".", "usr"); @@ -46,4 +35,6 @@ int main(void) { check_base_dir("/usr/lib", "/usr", "lib"); check_base_dir("//usr//lib//", "//usr", "lib"); check_base_dir("/home//dwc//test", "/home//dwc", "test"); + + return EXIT_SUCCESS; } -- cgit v1.2.3 From 76253d272c50ba08002a47395e47a4f9ce4fb53e Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 25 Sep 2023 13:58:19 -0400 Subject: Use the new list macros --- src/bftw.c | 24 +++++++++++------------- src/color.c | 2 +- src/ctx.c | 4 ++-- src/trie.h | 7 +++---- src/xspawn.c | 11 +++++------ tests/trie.c | 4 ++-- 6 files changed, 24 insertions(+), 28 deletions(-) (limited to 'src/ctx.c') diff --git a/src/bftw.c b/src/bftw.c index e6b8cd5..f3060ce 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -318,8 +318,7 @@ static size_t bftw_child_nameoff(const struct bftw_file *parent) { /** Destroy a cache. */ static void bftw_cache_destroy(struct bftw_cache *cache) { - bfs_assert(!cache->head); - bfs_assert(!cache->tail); + bfs_assert(LIST_EMPTY(cache)); bfs_assert(!cache->target); varena_destroy(&cache->files); @@ -347,9 +346,9 @@ static struct bftw_file *bftw_file_new(struct bftw_cache *cache, struct bftw_fil file->nameoff = 0; } - file->next = NULL; - file->to_read.next = NULL; - file->lru.prev = file->lru.next = NULL; + SLIST_ITEM_INIT(file); + SLIST_ITEM_INIT(file, to_read); + LIST_ITEM_INIT(file, lru); file->refcount = 1; file->pincount = 0; @@ -833,12 +832,11 @@ static void bftw_push_dir(struct bftw_state *state, struct bftw_file *file) { SLIST_APPEND(&state->to_read, file, to_read); } - while (state->to_open.head) { - if (bftw_ioq_opendir(state, state->to_open.head) == 0) { - SLIST_POP(&state->to_open); - } else { + for_slist (struct bftw_file, dir, &state->to_open) { + if (bftw_ioq_opendir(state, dir) != 0) { break; } + SLIST_POP(&state->to_open); } } @@ -847,7 +845,7 @@ static bool bftw_pop_dir(struct bftw_state *state) { bfs_assert(!state->file); struct bftw_cache *cache = &state->cache; - bool have_files = state->to_visit.head; + bool have_files = !SLIST_EMPTY(&state->to_visit); if (state->flags & BFTW_SORT) { // Keep strict breadth-first order when sorting @@ -855,9 +853,9 @@ static bool bftw_pop_dir(struct bftw_state *state) { return false; } } else { - while (!state->to_read.head) { + while (SLIST_EMPTY(&state->to_read)) { // Block if we have no other files/dirs to visit, or no room in the cache - bool have_dirs = state->to_open.head; + bool have_dirs = !SLIST_EMPTY(&state->to_open); bool have_room = cache->capacity > 0 && cache->dirlimit > 0; bool block = !(have_dirs || have_files) || !have_room; @@ -1303,7 +1301,7 @@ static void bftw_list_sort(struct bftw_list *list) { bftw_list_sort(&right); // Merge - while (left.head && right.head) { + while (!SLIST_EMPTY(&left) && !SLIST_EMPTY(&right)) { struct bftw_file *lf = left.head; struct bftw_file *rf = right.head; diff --git a/src/color.c b/src/color.c index b9a788b..5e78c6c 100644 --- a/src/color.c +++ b/src/color.c @@ -328,7 +328,7 @@ fail: static int build_iext_trie(struct colors *colors) { trie_clear(&colors->iext_trie); - TRIE_FOR_EACH(&colors->ext_trie, leaf) { + for_trie (leaf, &colors->ext_trie) { size_t len = leaf->length - 1; if (colors->ext_len < len) { colors->ext_len = len; diff --git a/src/ctx.c b/src/ctx.c index a940bed..3a44e68 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -152,7 +152,7 @@ void bfs_ctx_flush(const struct bfs_ctx *ctx) { // - the user sees everything relevant before an -ok[dir] prompt // - output from commands is interleaved consistently with bfs // - executed commands can rely on I/O from other bfs actions - TRIE_FOR_EACH(&ctx->files, leaf) { + for_trie (leaf, &ctx->files) { struct bfs_ctx_file *ctx_file = leaf->value; CFILE *cfile = ctx_file->cfile; if (fflush(cfile->file) == 0) { @@ -239,7 +239,7 @@ int bfs_ctx_free(struct bfs_ctx *ctx) { bfs_groups_free(ctx->groups); bfs_users_free(ctx->users); - TRIE_FOR_EACH(&ctx->files, leaf) { + for_trie (leaf, &ctx->files) { struct bfs_ctx_file *ctx_file = leaf->value; if (ctx_file->error) { diff --git a/src/trie.h b/src/trie.h index dfaae15..2f51db5 100644 --- a/src/trie.h +++ b/src/trie.h @@ -6,6 +6,7 @@ #include "config.h" #include "alloc.h" +#include "list.h" #include #include @@ -141,9 +142,7 @@ void trie_destroy(struct trie *trie); /** * Iterate over the leaves of a trie. */ -#define TRIE_FOR_EACH(trie, leaf) \ - for (struct trie_leaf *leaf = (trie)->head, *_next; \ - leaf && (_next = leaf->next, true); \ - leaf = _next) +#define for_trie(leaf, trie) \ + for_list(struct trie_leaf, leaf, trie) #endif // BFS_TRIE_H diff --git a/src/xspawn.c b/src/xspawn.c index 2cabdcc..80bafef 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -49,8 +49,8 @@ int bfs_spawn_init(struct bfs_spawn *ctx) { } int bfs_spawn_destroy(struct bfs_spawn *ctx) { - while (ctx->head) { - free(SLIST_POP(ctx)); + for_slist (struct bfs_spawn_action, action, ctx) { + free(action); } return 0; @@ -68,7 +68,7 @@ static struct bfs_spawn_action *bfs_spawn_add(struct bfs_spawn *ctx, enum bfs_sp return NULL; } - action->next = NULL; + SLIST_ITEM_INIT(action); action->op = op; action->in_fd = -1; action->out_fd = -1; @@ -138,7 +138,7 @@ int bfs_spawn_addsetrlimit(struct bfs_spawn *ctx, int resource, const struct rli static void bfs_spawn_exec(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp, int pipefd[2]) { xclose(pipefd[0]); - for (const struct bfs_spawn_action *action = ctx ? ctx->head : NULL; action; action = action->next) { + for_slist (const struct bfs_spawn_action, action, ctx) { // Move the error-reporting pipe out of the way if necessary... if (action->out_fd == pipefd[1]) { int fd = dup_cloexec(pipefd[1]); @@ -199,9 +199,8 @@ pid_t bfs_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char envp = environ; } - enum bfs_spawn_flags flags = ctx ? ctx->flags : 0; char *resolved = NULL; - if (flags & BFS_SPAWN_USEPATH) { + if (ctx->flags & BFS_SPAWN_USEPATH) { exe = resolved = bfs_spawn_resolve(exe); if (!resolved) { return -1; diff --git a/tests/trie.c b/tests/trie.c index e687f96..6ea94a2 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -70,7 +70,7 @@ int main(void) { { size_t i = 0; - TRIE_FOR_EACH(&trie, leaf) { + for_trie (leaf, &trie) { bfs_verify(leaf == trie_find_str(&trie, keys[i])); bfs_verify(!leaf->prev || leaf->prev->next == leaf); bfs_verify(!leaf->next || leaf->next->prev == leaf); @@ -107,7 +107,7 @@ int main(void) { } } - TRIE_FOR_EACH(&trie, leaf) { + for_trie (leaf, &trie) { bfs_verify(false); } -- cgit v1.2.3 From 52de184ba28551734e1cb13233588504ab5f62ec Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 27 Sep 2023 12:11:15 -0400 Subject: Formatting fixes --- src/color.c | 6 +++--- src/ctx.c | 1 - src/darray.c | 4 ++-- src/diag.c | 8 ++++---- src/diag.h | 2 +- src/dstring.c | 4 ++-- src/eval.c | 15 +++++++-------- src/exec.c | 16 ++++++++-------- src/fsade.c | 6 +++--- src/ioq.c | 2 +- src/opt.c | 11 +++++------ src/opt.h | 1 - src/parse.c | 45 ++++++++++++++++++++++----------------------- src/printf.c | 46 +++++++++++++++++++++++----------------------- src/pwcache.c | 1 - src/stat.c | 2 +- src/trie.c | 2 +- src/trie.h | 4 ++-- src/xregex.c | 2 +- src/xspawn.c | 2 +- src/xtime.c | 12 ++++++------ tests/bfstd.c | 2 +- tests/mksock.c | 2 +- tests/trie.c | 4 ++-- tests/xtimegm.c | 6 +++--- 25 files changed, 100 insertions(+), 106 deletions(-) (limited to 'src/ctx.c') diff --git a/src/color.c b/src/color.c index 8d0b995..788d35d 100644 --- a/src/color.c +++ b/src/color.c @@ -1143,11 +1143,11 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { if (verbose) { double rate = 0.0, time = 0.0; if (expr->evaluations) { - rate = 100.0*expr->successes/expr->evaluations; - time = (1.0e9*expr->elapsed.tv_sec + expr->elapsed.tv_nsec)/expr->evaluations; + rate = 100.0 * expr->successes / expr->evaluations; + time = (1.0e9 * expr->elapsed.tv_sec + expr->elapsed.tv_nsec) / expr->evaluations; } if (cbuff(cfile, " [${ylw}%zu${rs}/${ylw}%zu${rs}=${ylw}%g%%${rs}; ${ylw}%gns${rs}]", - expr->successes, expr->evaluations, rate, time)) { + expr->successes, expr->evaluations, rate, time)) { return -1; } } diff --git a/src/ctx.c b/src/ctx.c index 3a44e68..9a24a33 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -168,7 +168,6 @@ void bfs_ctx_flush(const struct bfs_ctx *ctx) { } else if (cfile == ctx->cout) { bfs_error(ctx, "(standard output): %m.\n"); } - } // Flush the user/group caches, in case the executed command edits the diff --git a/src/darray.c b/src/darray.c index 42b8397..3e66a55 100644 --- a/src/darray.c +++ b/src/darray.c @@ -55,7 +55,7 @@ void *darray_push(void *da, const void *item, size_t size) { size_t i = header->length++; if (i >= capacity) { capacity *= 2; - header = realloc(header, sizeof(*header) + capacity*size); + header = realloc(header, sizeof(*header) + capacity * size); if (!header) { // This failure will be detected by darray_check() return da; @@ -64,7 +64,7 @@ void *darray_push(void *da, const void *item, size_t size) { } char *data = darray_data(header); - memcpy(data + i*size, item, size); + memcpy(data + i * size, item, size); return data; } diff --git a/src/diag.c b/src/diag.c index fa9db39..fa66525 100644 --- a/src/diag.c +++ b/src/diag.c @@ -4,9 +4,9 @@ #include "diag.h" #include "alloc.h" #include "bfstd.h" -#include "ctx.h" #include "color.h" #include "config.h" +#include "ctx.h" #include "dstring.h" #include "expr.h" #include @@ -31,14 +31,14 @@ void bfs_perror(const struct bfs_ctx *ctx, const char *str) { bfs_error(ctx, "%s: %m.\n", str); } -void bfs_error(const struct bfs_ctx *ctx, const char *format, ...) { +void bfs_error(const struct bfs_ctx *ctx, const char *format, ...) { va_list args; va_start(args, format); bfs_verror(ctx, format, args); va_end(args); } -bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...) { +bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...) { va_list args; va_start(args, format); bool ret = bfs_vwarning(ctx, format, args); @@ -46,7 +46,7 @@ bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...) { return ret; } -bool bfs_debug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, ...) { +bool bfs_debug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, ...) { va_list args; va_start(args, format); bool ret = bfs_vdebug(ctx, flag, format, args); diff --git a/src/diag.h b/src/diag.h index e019db0..fea8847 100644 --- a/src/diag.h +++ b/src/diag.h @@ -8,8 +8,8 @@ #ifndef BFS_DIAG_H #define BFS_DIAG_H -#include "ctx.h" #include "config.h" +#include "ctx.h" #include /** diff --git a/src/dstring.c b/src/dstring.c index ef4e733..f947741 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -169,7 +169,7 @@ char *dstrprintf(const char *format, ...) { char *dstrvprintf(const char *format, va_list args) { // Guess a capacity to try to avoid reallocating - dchar *str = dstralloc(2*strlen(format)); + dchar *str = dstralloc(2 * strlen(format)); if (!str) { return NULL; } @@ -195,7 +195,7 @@ int dstrcatf(dchar **str, const char *format, ...) { int dstrvcatf(dchar **str, const char *format, va_list args) { // Guess a capacity to try to avoid calling vsnprintf() twice size_t len = dstrlen(*str); - dstreserve(str, len + 2*strlen(format)); + dstreserve(str, len + 2 * strlen(format)); size_t cap = dstrheader(*str)->capacity; va_list copy; diff --git a/src/eval.c b/src/eval.c index 3550751..9f4896a 100644 --- a/src/eval.c +++ b/src/eval.c @@ -239,7 +239,7 @@ bool eval_time(const struct bfs_expr *expr, struct bfs_eval *state) { time_t diff = timespec_diff(&expr->reftime, time); switch (expr->time_unit) { case BFS_DAYS: - diff /= 60*24; + diff /= 60 * 24; fallthru; case BFS_MINUTES: diff /= 60; @@ -271,7 +271,7 @@ bool eval_used(const struct bfs_expr *expr, struct bfs_eval *state) { return false; } - long long day_seconds = 60*60*24; + long long day_seconds = 60 * 60 * 24; diff = (diff + day_seconds - 1) / day_seconds; return bfs_expr_cmp(expr, diff); } @@ -685,7 +685,7 @@ bool eval_fls(const struct bfs_expr *expr, struct bfs_eval *state) { uintmax_t ino = statbuf->ino; uintmax_t block_size = ctx->posixly_correct ? 512 : 1024; - uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + block_size - 1)/block_size; + uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + block_size - 1) / block_size; char mode[11]; xstrmode(statbuf->mode, mode); char acl = bfs_check_acl(ftwbuf) > 0 ? '+' : ' '; @@ -721,8 +721,8 @@ bool eval_fls(const struct bfs_expr *expr, struct bfs_eval *state) { time_t time = statbuf->mtime.tv_sec; time_t now = ctx->now.tv_sec; - time_t six_months_ago = now - 6*30*24*60*60; - time_t tomorrow = now + 24*60*60; + time_t six_months_ago = now - 6 * 30 * 24 * 60 * 60; + time_t tomorrow = now + 24 * 60 * 60; struct tm tm; if (xlocaltime(&time, &tm) != 0) { goto error; @@ -823,7 +823,6 @@ bool eval_fprintx(const struct bfs_expr *expr, struct bfs_eval *state) { ++path; } - if (fputc('\n', file) == EOF) { goto error; } @@ -905,7 +904,7 @@ bool eval_size(const struct bfs_expr *expr, struct bfs_eval *state) { }; off_t scale = scales[expr->size_unit]; - off_t size = (statbuf->size + scale - 1)/scale; // Round up + off_t size = (statbuf->size + scale - 1) / scale; // Round up return bfs_expr_cmp(expr, size); } @@ -918,7 +917,7 @@ bool eval_sparse(const struct bfs_expr *expr, struct bfs_eval *state) { return false; } - blkcnt_t expected = (statbuf->size + BFS_STAT_BLKSIZE - 1)/BFS_STAT_BLKSIZE; + blkcnt_t expected = (statbuf->size + BFS_STAT_BLKSIZE - 1) / BFS_STAT_BLKSIZE; return statbuf->blocks < expected; } diff --git a/src/exec.c b/src/exec.c index 7b55522..0e0d585 100644 --- a/src/exec.c +++ b/src/exec.c @@ -5,9 +5,9 @@ #include "alloc.h" #include "bfstd.h" #include "bftw.h" -#include "ctx.h" #include "color.h" #include "config.h" +#include "ctx.h" #include "diag.h" #include "dstring.h" #include "xspawn.h" @@ -348,7 +348,7 @@ static int bfs_exec_spawn(const struct bfs_exec *execbuf) { if (execbuf->flags & BFS_EXEC_MULTI) { bfs_exec_debug(execbuf, "Executing '%s' ... [%zu arguments] (size %zu)\n", - execbuf->argv[0], execbuf->argc - 1, execbuf->arg_size); + execbuf->argv[0], execbuf->argc - 1, execbuf->arg_size); } else { bfs_exec_debug(execbuf, "Executing '%s' ... [%zu arguments]\n", execbuf->argv[0], execbuf->argc - 1); } @@ -471,7 +471,7 @@ static bool bfs_exec_args_remain(const struct bfs_exec *execbuf) { static size_t bfs_exec_estimate_max(const struct bfs_exec *execbuf) { size_t min = execbuf->arg_min; size_t max = execbuf->arg_max; - return min + (max - min)/2; + return min + (max - min) / 2; } /** Update the ARG_MAX lower bound from a successful execution. */ @@ -486,7 +486,7 @@ static void bfs_exec_update_min(struct bfs_exec *execbuf) { size_t estimate = bfs_exec_estimate_max(execbuf); bfs_exec_debug(execbuf, "ARG_MAX between [%zu, %zu], trying %zu\n", - execbuf->arg_min, execbuf->arg_max, estimate); + execbuf->arg_min, execbuf->arg_max, estimate); } } @@ -502,7 +502,7 @@ static size_t bfs_exec_update_max(struct bfs_exec *execbuf) { // Trim a fraction off the max size to avoid repeated failures near the // top end of the working range - size -= size/16; + size -= size / 16; if (size < execbuf->arg_max) { execbuf->arg_max = size; @@ -515,7 +515,7 @@ static size_t bfs_exec_update_max(struct bfs_exec *execbuf) { // Binary search for a more precise bound size_t estimate = bfs_exec_estimate_max(execbuf); bfs_exec_debug(execbuf, "ARG_MAX between [%zu, %zu], trying %zu\n", - execbuf->arg_min, execbuf->arg_max, estimate); + execbuf->arg_min, execbuf->arg_max, estimate); return estimate; } @@ -589,7 +589,7 @@ static bool bfs_exec_would_overflow(const struct bfs_exec *execbuf, const char * size_t next_size = execbuf->arg_size + bfs_exec_arg_size(arg); if (next_size > arg_max) { bfs_exec_debug(execbuf, "Command size (%zu) would exceed maximum (%zu), executing buffered command\n", - next_size, arg_max); + next_size, arg_max); return true; } @@ -601,7 +601,7 @@ static int bfs_exec_push(struct bfs_exec *execbuf, char *arg) { execbuf->argv[execbuf->argc] = arg; if (execbuf->argc + 1 >= execbuf->argv_cap) { - size_t cap = 2*execbuf->argv_cap; + size_t cap = 2 * execbuf->argv_cap; char **argv = realloc(execbuf->argv, sizeof_array(char *, cap)); if (!argv) { return -1; diff --git a/src/fsade.c b/src/fsade.c index 8dec5a8..cbff47b 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -3,9 +3,9 @@ #include "fsade.h" #include "atomic.h" -#include "config.h" #include "bfstd.h" #include "bftw.h" +#include "config.h" #include "dir.h" #include "dstring.h" #include "sanity.h" @@ -292,7 +292,7 @@ int bfs_check_xattrs(const struct BFTW *ftwbuf) { ssize_t len; #if BFS_USE_SYS_EXTATTR_H - ssize_t (*extattr_list)(const char *, int, void*, size_t) = + ssize_t (*extattr_list)(const char *, int, void *, size_t) = ftwbuf->type == BFS_LNK ? extattr_list_link : extattr_list_file; len = extattr_list(path, EXTATTR_NAMESPACE_SYSTEM, NULL, 0); @@ -331,7 +331,7 @@ int bfs_check_xattr_named(const struct BFTW *ftwbuf, const char *name) { ssize_t len; #if BFS_USE_SYS_EXTATTR_H - ssize_t (*extattr_get)(const char *, int, const char *, void*, size_t) = + ssize_t (*extattr_get)(const char *, int, const char *, void *, size_t) = ftwbuf->type == BFS_LNK ? extattr_get_link : extattr_get_file; len = extattr_get(path, EXTATTR_NAMESPACE_SYSTEM, name, NULL, 0); diff --git a/src/ioq.c b/src/ioq.c index f7ca8c6..d3ba2de 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -9,8 +9,8 @@ #include "config.h" #include "diag.h" #include "dir.h" -#include "thread.h" #include "sanity.h" +#include "thread.h" #include #include #include diff --git a/src/opt.c b/src/opt.c index 14de081..77c2798 100644 --- a/src/opt.c +++ b/src/opt.c @@ -535,8 +535,8 @@ static struct bfs_expr *optimize_and_expr(const struct opt_state *state, struct expr->pure = lhs->pure && rhs->pure; expr->always_true = lhs->always_true && rhs->always_true; expr->always_false = lhs->always_false || rhs->always_false; - expr->cost = lhs->cost + lhs->probability*rhs->cost; - expr->probability = lhs->probability*rhs->probability; + expr->cost = lhs->cost + lhs->probability * rhs->cost; + expr->probability = lhs->probability * rhs->probability; return expr; } @@ -606,8 +606,8 @@ static struct bfs_expr *optimize_or_expr(const struct opt_state *state, struct b expr->pure = lhs->pure && rhs->pure; expr->always_true = lhs->always_true || rhs->always_true; expr->always_false = lhs->always_false && rhs->always_false; - expr->cost = lhs->cost + (1 - lhs->probability)*rhs->cost; - expr->probability = lhs->probability + rhs->probability - lhs->probability*rhs->probability; + expr->cost = lhs->cost + (1 - lhs->probability) * rhs->cost; + expr->probability = lhs->probability + rhs->probability - lhs->probability * rhs->probability; return expr; } @@ -1078,7 +1078,6 @@ static const struct { {eval_xattrname, 0.01}, }; - /** * Table of simple predicates. */ @@ -1331,7 +1330,7 @@ static bool reorder_expr_recursive(const struct opt_state *state, struct bfs_exp if (expr->eval_fn == eval_and || expr->eval_fn == eval_or) { if (lhs->pure && rhs->pure) { float rhs_prob = expr->eval_fn == eval_and ? rhs->probability : 1.0 - rhs->probability; - float swapped_cost = rhs->cost + rhs_prob*lhs->cost; + float swapped_cost = rhs->cost + rhs_prob * lhs->cost; ret |= reorder_expr(state, expr, swapped_cost); } } diff --git a/src/opt.h b/src/opt.h index 28cadb9..4aac129 100644 --- a/src/opt.h +++ b/src/opt.h @@ -21,4 +21,3 @@ struct bfs_ctx; int bfs_optimize(struct bfs_ctx *ctx); #endif // BFS_OPT_H - diff --git a/src/parse.c b/src/parse.c index 3416d9e..7766a7b 100644 --- a/src/parse.c +++ b/src/parse.c @@ -41,8 +41,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -1797,10 +1797,10 @@ static int parse_reftime(const struct parser_state *state, struct bfs_expr *expr #else int gmtoff = -timezone; #endif - int tz_hour = gmtoff/3600; - int tz_min = (labs(gmtoff)/60)%60; + int tz_hour = gmtoff / 3600; + int tz_min = (labs(gmtoff) / 60) % 60; fprintf(stderr, " - %04d-%02d-%02dT%02d:%02d:%02d%+03d:%02d\n", - year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tz_hour, tz_min); + year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tz_hour, tz_min); if (xgmtime(&state->now.tv_sec, &tm) != 0) { parse_perror(state, "xgmtime()"); @@ -1832,8 +1832,8 @@ static struct bfs_expr *parse_newerxy(struct parser_state *state, int arg1, int expr->stat_field = parse_newerxy_field(arg[6]); if (!expr->stat_field) { parse_expr_error(state, expr, - "For ${blu}-newer${bld}XY${rs}, ${bld}X${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, or ${bld}B${rs}, not ${err}%c${rs}.\n", - arg[6]); + "For ${blu}-newer${bld}XY${rs}, ${bld}X${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, or ${bld}B${rs}, not ${err}%c${rs}.\n", + arg[6]); goto fail; } @@ -1845,8 +1845,8 @@ static struct bfs_expr *parse_newerxy(struct parser_state *state, int arg1, int enum bfs_stat_field field = parse_newerxy_field(arg[7]); if (!field) { parse_expr_error(state, expr, - "For ${blu}-newer${bld}XY${rs}, ${bld}Y${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, ${bld}B${rs}, or ${bld}t${rs}, not ${err}%c${rs}.\n", - arg[7]); + "For ${blu}-newer${bld}XY${rs}, ${bld}Y${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, ${bld}B${rs}, or ${bld}t${rs}, not ${err}%c${rs}.\n", + arg[7]); goto fail; } @@ -1855,7 +1855,6 @@ static struct bfs_expr *parse_newerxy(struct parser_state *state, int arg1, int goto fail; } - const struct timespec *reftime = bfs_stat_time(&sb, field); if (!reftime) { parse_expr_error(state, expr, "Couldn't get file %s.\n", bfs_stat_field_name(field)); @@ -1905,7 +1904,7 @@ static struct bfs_expr *parse_nohidden(struct parser_state *state, int arg1, int */ static struct bfs_expr *parse_noleaf(struct parser_state *state, int arg1, int arg2) { parse_warning(state, "${ex}%s${rs} does not apply the optimization that ${blu}%s${rs} inhibits.\n\n", - BFS_COMMAND, state->argv[0]); + BFS_COMMAND, state->argv[0]); return parse_nullary_option(state); } @@ -2730,12 +2729,12 @@ static struct bfs_expr *parse_help(struct parser_state *state, int arg1, int arg } cfprintf(cout, "Usage: ${ex}%s${rs} [${cyn}flags${rs}...] [${mag}paths${rs}...] [${blu}expression${rs}...]\n\n", - state->command); + state->command); cfprintf(cout, "${ex}%s${rs} is compatible with ${ex}find${rs}, with some extensions. " - "${cyn}Flags${rs} (${cyn}-H${rs}/${cyn}-L${rs}/${cyn}-P${rs} etc.), ${mag}paths${rs},\n" - "and ${blu}expressions${rs} may be freely mixed in any order.\n\n", - BFS_COMMAND); + "${cyn}Flags${rs} (${cyn}-H${rs}/${cyn}-L${rs}/${cyn}-P${rs} etc.), ${mag}paths${rs},\n" + "and ${blu}expressions${rs} may be freely mixed in any order.\n\n", + BFS_COMMAND); cfprintf(cout, "${bld}Flags:${rs}\n\n"); @@ -2807,7 +2806,7 @@ static struct bfs_expr *parse_help(struct parser_state *state, int arg1, int arg cfprintf(cout, " ${blu}-ignore_readdir_race${rs}\n"); cfprintf(cout, " ${blu}-noignore_readdir_race${rs}\n"); cfprintf(cout, " Whether to report an error if ${ex}%s${rs} detects that the file tree is modified\n", - BFS_COMMAND); + BFS_COMMAND); cfprintf(cout, " during the search (default: ${blu}-noignore_readdir_race${rs})\n"); cfprintf(cout, " ${blu}-maxdepth${rs} ${bld}N${rs}\n"); cfprintf(cout, " ${blu}-mindepth${rs} ${bld}N${rs}\n"); @@ -3462,14 +3461,14 @@ static struct bfs_expr *parse_whole_expr(struct parser_state *state) { if (state->mount_arg && state->xdev_arg) { parse_conflict_warning(state, state->mount_arg, 1, state->xdev_arg, 1, - "${blu}%s${rs} is redundant in the presence of ${blu}%s${rs}.\n\n", - state->xdev_arg[0], state->mount_arg[0]); + "${blu}%s${rs} is redundant in the presence of ${blu}%s${rs}.\n\n", + state->xdev_arg[0], state->mount_arg[0]); } if (state->ctx->warn && state->depth_arg && state->prune_arg) { parse_conflict_warning(state, state->depth_arg, 1, state->prune_arg, 1, - "${blu}%s${rs} does not work in the presence of ${blu}%s${rs}.\n", - state->prune_arg[0], state->depth_arg[0]); + "${blu}%s${rs} does not work in the presence of ${blu}%s${rs}.\n", + state->prune_arg[0], state->depth_arg[0]); if (state->interactive) { bfs_warning(state->ctx, "Do you want to continue? "); @@ -3483,8 +3482,8 @@ static struct bfs_expr *parse_whole_expr(struct parser_state *state) { if (state->ok_expr && state->files0_stdin_arg) { parse_conflict_error(state, state->ok_expr->argv, state->ok_expr->argc, state->files0_stdin_arg, 2, - "${blu}%s${rs} conflicts with ${blu}%s${rs} ${bld}%s${rs}.\n", - state->ok_expr->argv[0], state->files0_stdin_arg[0], state->files0_stdin_arg[1]); + "${blu}%s${rs} conflicts with ${blu}%s${rs} ${bld}%s${rs}.\n", + state->ok_expr->argv[0], state->files0_stdin_arg[0], state->files0_stdin_arg[1]); goto fail; } @@ -3644,7 +3643,7 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) { static void dump_costs(const struct bfs_ctx *ctx) { const struct bfs_expr *expr = ctx->expr; bfs_debug(ctx, DEBUG_COST, " Cost: ~${ylw}%g${rs}\n", expr->cost); - bfs_debug(ctx, DEBUG_COST, "Probability: ~${ylw}%g%%${rs}\n", 100.0*expr->probability); + bfs_debug(ctx, DEBUG_COST, "Probability: ~${ylw}%g%%${rs}\n", 100.0 * expr->probability); } struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { @@ -3654,7 +3653,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { goto fail; } - static char* default_argv[] = {BFS_COMMAND, NULL}; + static char *default_argv[] = {BFS_COMMAND, NULL}; if (argc < 1) { argc = 1; argv = default_argv; diff --git a/src/printf.c b/src/printf.c index 5de5a28..98bcb0f 100644 --- a/src/printf.c +++ b/src/printf.c @@ -113,14 +113,14 @@ static int bfs_printf_ctime(CFILE *cfile, const struct bfs_printf *directive, co } BFS_PRINTF_BUF(buf, "%s %s %2d %.2d:%.2d:%.2d.%09ld0 %4d", - days[tm.tm_wday], - months[tm.tm_mon], - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - (long)ts->tv_nsec, - 1900 + tm.tm_year); + days[tm.tm_wday], + months[tm.tm_mon], + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + (long)ts->tv_nsec, + 1900 + tm.tm_year); return dyn_fprintf(cfile->file, directive, buf); } @@ -152,19 +152,19 @@ static int bfs_printf_strftime(CFILE *cfile, const struct bfs_printf *directive, break; case '+': ret = snprintf(buf, sizeof(buf), "%4d-%.2d-%.2d+%.2d:%.2d:%.2d.%09ld0", - 1900 + tm.tm_year, - tm.tm_mon + 1, - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - (long)ts->tv_nsec); + 1900 + tm.tm_year, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + (long)ts->tv_nsec); break; case 'k': ret = snprintf(buf, sizeof(buf), "%2d", tm.tm_hour); break; case 'l': - ret = snprintf(buf, sizeof(buf), "%2d", (tm.tm_hour + 11)%12 + 1); + ret = snprintf(buf, sizeof(buf), "%2d", (tm.tm_hour + 11) % 12 + 1); break; case 's': ret = snprintf(buf, sizeof(buf), "%lld", (long long)ts->tv_sec); @@ -174,10 +174,10 @@ static int bfs_printf_strftime(CFILE *cfile, const struct bfs_printf *directive, break; case 'T': ret = snprintf(buf, sizeof(buf), "%.2d:%.2d:%.2d.%09ld0", - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - (long)ts->tv_nsec); + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + (long)ts->tv_nsec); break; // POSIX strftime() features @@ -202,7 +202,7 @@ static int bfs_printf_b(CFILE *cfile, const struct bfs_printf *directive, const return -1; } - uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + 511)/512; + uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + 511) / 512; BFS_PRINTF_BUF(buf, "%ju", blocks); return dyn_fprintf(cfile->file, directive, buf); } @@ -338,7 +338,7 @@ static int bfs_printf_k(CFILE *cfile, const struct bfs_printf *directive, const return -1; } - uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + 1023)/1024; + uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + 1023) / 1024; BFS_PRINTF_BUF(buf, "%ju", blocks); return dyn_fprintf(cfile->file, directive, buf); } @@ -452,7 +452,7 @@ static int bfs_printf_S(CFILE *cfile, const struct bfs_printf *directive, const if (statbuf->size == 0 && statbuf->blocks == 0) { sparsity = 1.0; } else { - sparsity = (double)BFS_STAT_BLKSIZE*statbuf->blocks/statbuf->size; + sparsity = (double)BFS_STAT_BLKSIZE * statbuf->blocks / statbuf->size; } return dyn_fprintf(cfile->file, directive, sparsity); } diff --git a/src/pwcache.c b/src/pwcache.c index 0e2f5c1..c728ba9 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -4,7 +4,6 @@ #include "pwcache.h" #include "alloc.h" #include "config.h" -#include "darray.h" #include "trie.h" #include #include diff --git a/src/stat.c b/src/stat.c index e8f48ee..d7387c6 100644 --- a/src/stat.c +++ b/src/stat.c @@ -10,8 +10,8 @@ #include #include #include -#include #include +#include #if defined(STATX_BASIC_STATS) && (!__ANDROID__ || __ANDROID_API__ >= 30) # define BFS_HAS_LIBC_STATX true diff --git a/src/trie.c b/src/trie.c index 77aa2d0..55544e6 100644 --- a/src/trie.c +++ b/src/trie.c @@ -700,7 +700,7 @@ static void trie_remove_impl(struct trie *trie, struct trie_leaf *leaf) { } if (child_index < parent_size) { - memmove(child, child + 1, (parent_size - child_index)*sizeof(*child)); + memmove(child, child + 1, (parent_size - child_index) * sizeof(*child)); } if (has_single_bit(parent_size)) { diff --git a/src/trie.h b/src/trie.h index 2f51db5..02088f1 100644 --- a/src/trie.h +++ b/src/trie.h @@ -4,8 +4,8 @@ #ifndef BFS_TRIE_H #define BFS_TRIE_H -#include "config.h" #include "alloc.h" +#include "config.h" #include "list.h" #include #include @@ -143,6 +143,6 @@ void trie_destroy(struct trie *trie); * Iterate over the leaves of a trie. */ #define for_trie(leaf, trie) \ - for_list(struct trie_leaf, leaf, trie) + for_list (struct trie_leaf, leaf, trie) #endif // BFS_TRIE_H diff --git a/src/xregex.c b/src/xregex.c index beb6676..b9c04bf 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -5,8 +5,8 @@ #include "alloc.h" #include "config.h" #include "diag.h" -#include "thread.h" #include "sanity.h" +#include "thread.h" #include #include #include diff --git a/src/xspawn.c b/src/xspawn.c index 80bafef..7fb63e0 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -187,7 +187,7 @@ fail: // In case of a write error, the parent will still see that we exited // unsuccessfully, but won't know why - (void) xwrite(pipefd[1], &error, sizeof(error)); + (void)xwrite(pipefd[1], &error, sizeof(error)); xclose(pipefd[1]); _Exit(127); diff --git a/src/xtime.c b/src/xtime.c index 79dafad..e90bdb1 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -81,7 +81,7 @@ static int safe_add(int *value, int delta) { static int floor_div(int n, int d) { int a = n < 0; - return (n + a)/d - a; + return (n + a) / d - a; } static int wrap(int *value, int max, int *next) { @@ -93,7 +93,7 @@ static int wrap(int *value, int max, int *next) { static int month_length(int year, int month) { static const int month_lengths[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int ret = month_lengths[month]; - if (month == 1 && year%4 == 0 && (year%100 != 0 || (year + 300)%400 == 0)) { + if (month == 1 && year % 4 == 0 && (year % 100 != 0 || (year + 300) % 400 == 0)) { ++ret; } return ret; @@ -156,13 +156,13 @@ int xtimegm(struct tm *tm, time_t *timep) { leap_days = floor_div(tm->tm_year + 3, 4) - floor_div(tm->tm_year + 99, 100) + floor_div(tm->tm_year + 299, 400) - 17; } - long long epoch_days = 365LL*(tm->tm_year - 70) + leap_days + tm->tm_yday; - tm->tm_wday = (epoch_days + 4)%7; + long long epoch_days = 365LL * (tm->tm_year - 70) + leap_days + tm->tm_yday; + tm->tm_wday = (epoch_days + 4) % 7; if (tm->tm_wday < 0) { tm->tm_wday += 7; } - long long epoch_time = tm->tm_sec + 60*(tm->tm_min + 60*(tm->tm_hour + 24*epoch_days)); + long long epoch_time = tm->tm_sec + 60 * (tm->tm_min + 60 * (tm->tm_hour + 24 * epoch_days)); *timep = (time_t)epoch_time; if ((long long)*timep != epoch_time) { goto overflow; @@ -296,7 +296,7 @@ end: goto error; } - int offset = 60*tz_hour + tz_min; + int offset = 60 * tz_hour + tz_min; if (tz_negative) { result->tv_sec -= offset; } else { diff --git a/tests/bfstd.c b/tests/bfstd.c index 83964e5..33b3792 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -7,8 +7,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/tests/mksock.c b/tests/mksock.c index 42ef322..7023b4f 100644 --- a/tests/mksock.c +++ b/tests/mksock.c @@ -9,8 +9,8 @@ #include "../src/bfstd.h" #include #include -#include #include +#include #include #include #include diff --git a/tests/trie.c b/tests/trie.c index 6ea94a2..656fd85 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -120,11 +120,11 @@ int main(void) { bfs_verify(!trie_find_mem(&trie, longstr, longsize)); bfs_verify(trie_insert_mem(&trie, longstr, longsize)); - memset(longstr + longsize/2, 0xAB, longsize/2); + memset(longstr + longsize / 2, 0xAB, longsize / 2); bfs_verify(!trie_find_mem(&trie, longstr, longsize)); bfs_verify(trie_insert_mem(&trie, longstr, longsize)); - memset(longstr, 0xAA, longsize/2); + memset(longstr, 0xAA, longsize / 2); bfs_verify(!trie_find_mem(&trie, longstr, longsize)); bfs_verify(trie_insert_mem(&trie, longstr, longsize)); diff --git a/tests/xtimegm.c b/tests/xtimegm.c index b2479b7..973b2eb 100644 --- a/tests/xtimegm.c +++ b/tests/xtimegm.c @@ -42,9 +42,9 @@ static bool tm_equal(const struct tm *tma, const struct tm *tmb) { static void tm_print(FILE *file, const struct tm *tm) { fprintf(file, "Y%d M%d D%d h%d m%d s%d wd%d yd%d%s\n", - tm->tm_year, tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - tm->tm_wday, tm->tm_yday, + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + tm->tm_wday, tm->tm_yday, tm->tm_isdst ? (tm->tm_isdst < 0 ? " (DST?)" : " (DST)") : ""); } -- cgit v1.2.3 From da5c9dd34f65989c842cfb831b8592157dd8ed34 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 12 Oct 2023 13:09:11 -0400 Subject: diag: Move enum debug_flags out of ctx.h --- src/ctx.c | 25 ------------------------- src/ctx.h | 28 +--------------------------- src/diag.c | 25 +++++++++++++++++++++++++ src/diag.h | 29 ++++++++++++++++++++++++++++- tests/alloc.c | 1 + 5 files changed, 55 insertions(+), 53 deletions(-) (limited to 'src/ctx.c') diff --git a/src/ctx.c b/src/ctx.c index 9a24a33..561ecae 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -17,31 +17,6 @@ #include #include -const char *debug_flag_name(enum debug_flags flag) { - switch (flag) { - case DEBUG_COST: - return "cost"; - case DEBUG_EXEC: - return "exec"; - case DEBUG_OPT: - return "opt"; - case DEBUG_RATES: - return "rates"; - case DEBUG_SEARCH: - return "search"; - case DEBUG_STAT: - return "stat"; - case DEBUG_TREE: - return "tree"; - - case DEBUG_ALL: - break; - } - - bfs_bug("Unrecognized debug flag"); - return "???"; -} - struct bfs_ctx *bfs_ctx_new(void) { struct bfs_ctx *ctx = ZALLOC(struct bfs_ctx); if (!ctx) { diff --git a/src/ctx.h b/src/ctx.h index 2b8e8cb..96406bd 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -10,38 +10,12 @@ #include "bftw.h" #include "config.h" +#include "diag.h" #include "trie.h" #include #include #include -/** - * Various debugging flags. - */ -enum debug_flags { - /** Print cost estimates. */ - DEBUG_COST = 1 << 0, - /** Print executed command details. */ - DEBUG_EXEC = 1 << 1, - /** Print optimization details. */ - DEBUG_OPT = 1 << 2, - /** Print rate information. */ - DEBUG_RATES = 1 << 3, - /** Trace the filesystem traversal. */ - DEBUG_SEARCH = 1 << 4, - /** Trace all stat() calls. */ - DEBUG_STAT = 1 << 5, - /** Print the parse tree. */ - DEBUG_TREE = 1 << 6, - /** All debug flags. */ - DEBUG_ALL = (1 << 7) - 1, -}; - -/** - * Convert a debug flag to a string. - */ -const char *debug_flag_name(enum debug_flags flag); - /** * The execution context for bfs. */ diff --git a/src/diag.c b/src/diag.c index fa66525..bf2343d 100644 --- a/src/diag.c +++ b/src/diag.c @@ -27,6 +27,31 @@ noreturn void bfs_abortf(const struct bfs_loc *loc, const char *format, ...) { abort(); } +const char *debug_flag_name(enum debug_flags flag) { + switch (flag) { + case DEBUG_COST: + return "cost"; + case DEBUG_EXEC: + return "exec"; + case DEBUG_OPT: + return "opt"; + case DEBUG_RATES: + return "rates"; + case DEBUG_SEARCH: + return "search"; + case DEBUG_STAT: + return "stat"; + case DEBUG_TREE: + return "tree"; + + case DEBUG_ALL: + break; + } + + bfs_bug("Unrecognized debug flag"); + return "???"; +} + void bfs_perror(const struct bfs_ctx *ctx, const char *str) { bfs_error(ctx, "%s: %m.\n", str); } diff --git a/src/diag.h b/src/diag.h index fea8847..838a794 100644 --- a/src/diag.h +++ b/src/diag.h @@ -9,7 +9,6 @@ #define BFS_DIAG_H #include "config.h" -#include "ctx.h" #include /** @@ -84,8 +83,36 @@ noreturn void bfs_abortf(const struct bfs_loc *loc, const char *format, ...); # define bfs_assert bfs_verify #endif +struct bfs_ctx; struct bfs_expr; +/** + * Various debugging flags. + */ +enum debug_flags { + /** Print cost estimates. */ + DEBUG_COST = 1 << 0, + /** Print executed command details. */ + DEBUG_EXEC = 1 << 1, + /** Print optimization details. */ + DEBUG_OPT = 1 << 2, + /** Print rate information. */ + DEBUG_RATES = 1 << 3, + /** Trace the filesystem traversal. */ + DEBUG_SEARCH = 1 << 4, + /** Trace all stat() calls. */ + DEBUG_STAT = 1 << 5, + /** Print the parse tree. */ + DEBUG_TREE = 1 << 6, + /** All debug flags. */ + DEBUG_ALL = (1 << 7) - 1, +}; + +/** + * Convert a debug flag to a string. + */ +const char *debug_flag_name(enum debug_flags flag); + /** * Like perror(), but decorated like bfs_error(). */ diff --git a/tests/alloc.c b/tests/alloc.c index 382131f..2334241 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -5,6 +5,7 @@ #include "../src/diag.h" #include #include +#include int main(void) { // Check sizeof_flex() -- cgit v1.2.3 From ad1b36291fe87ca9d647980e3afdc71344d66519 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 6 Nov 2023 10:06:41 -0500 Subject: ctx: Store the original and current RLIMIT_NOFILE --- src/ctx.c | 6 ++---- src/ctx.h | 8 ++++---- src/eval.c | 33 +++++++++++++++++++-------------- src/eval.h | 2 +- src/exec.c | 26 ++++++++++++-------------- 5 files changed, 38 insertions(+), 37 deletions(-) (limited to 'src/ctx.c') diff --git a/src/ctx.c b/src/ctx.c index 561ecae..692f5c5 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -30,12 +30,10 @@ struct bfs_ctx *bfs_ctx_new(void) { trie_init(&ctx->files); - struct rlimit rl; - if (getrlimit(RLIMIT_NOFILE, &rl) != 0) { + if (getrlimit(RLIMIT_NOFILE, &ctx->orig_nofile) != 0) { goto fail; } - ctx->nofile_soft = rl.rlim_cur; - ctx->nofile_hard = rl.rlim_max; + ctx->cur_nofile = ctx->orig_nofile; ctx->users = bfs_users_new(); if (!ctx->users) { diff --git a/src/ctx.h b/src/ctx.h index 96406bd..1c7df63 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -87,10 +87,10 @@ struct bfs_ctx { /** The number of files owned by the context. */ int nfiles; - /** The initial RLIMIT_NOFILE soft limit. */ - rlim_t nofile_soft; - /** The initial RLIMIT_NOFILE hard limit. */ - rlim_t nofile_hard; + /** The initial RLIMIT_NOFILE limits. */ + struct rlimit orig_nofile; + /** The current RLIMIT_NOFILE limits. */ + struct rlimit cur_nofile; /** The current time. */ struct timespec now; diff --git a/src/eval.c b/src/eval.c index 5ba3de8..b511eba 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1428,25 +1428,30 @@ done: } /** Raise RLIMIT_NOFILE if possible, and return the new limit. */ -static int raise_fdlimit(const struct bfs_ctx *ctx) { +static int raise_fdlimit(struct bfs_ctx *ctx) { + rlim_t cur = ctx->orig_nofile.rlim_cur; + rlim_t max = ctx->orig_nofile.rlim_max; + rlim_t target = 64 << 10; - if (rlim_cmp(target, ctx->nofile_hard) > 0) { - target = ctx->nofile_hard; + if (rlim_cmp(target, max) > 0) { + target = max; } - int ret = target; + if (rlim_cmp(target, cur) <= 0) { + return target; + } - if (rlim_cmp(target, ctx->nofile_soft) > 0) { - const struct rlimit rl = { - .rlim_cur = target, - .rlim_max = ctx->nofile_hard, - }; - if (setrlimit(RLIMIT_NOFILE, &rl) != 0) { - ret = ctx->nofile_soft; - } + const struct rlimit rl = { + .rlim_cur = target, + .rlim_max = max, + }; + + if (setrlimit(RLIMIT_NOFILE, &rl) != 0) { + return cur; } - return ret; + ctx->cur_nofile = rl; + return target; } /** Preallocate the fd table in the kernel. */ @@ -1581,7 +1586,7 @@ static bool eval_must_buffer(const struct bfs_expr *expr) { return false; } -int bfs_eval(const struct bfs_ctx *ctx) { +int bfs_eval(struct bfs_ctx *ctx) { if (!ctx->expr) { return EXIT_SUCCESS; } diff --git a/src/eval.h b/src/eval.h index bdb9440..98bbc08 100644 --- a/src/eval.h +++ b/src/eval.h @@ -39,7 +39,7 @@ typedef bool bfs_eval_fn(const struct bfs_expr *expr, struct bfs_eval *state); * @return * EXIT_SUCCESS on success, otherwise on failure. */ -int bfs_eval(const struct bfs_ctx *ctx); +int bfs_eval(struct bfs_ctx *ctx); // Predicate evaluation functions diff --git a/src/exec.c b/src/exec.c index 97cfafa..a3297e8 100644 --- a/src/exec.c +++ b/src/exec.c @@ -327,8 +327,10 @@ static void bfs_exec_closewd(struct bfs_exec *execbuf, const struct BFTW *ftwbuf /** Actually spawn the process. */ static int bfs_exec_spawn(const struct bfs_exec *execbuf) { + const struct bfs_ctx *ctx = execbuf->ctx; + // Flush the context state for consistency with the external process - bfs_ctx_flush(execbuf->ctx); + bfs_ctx_flush(ctx); if (execbuf->flags & BFS_EXEC_CONFIRM) { for (size_t i = 0; i < execbuf->argc; ++i) { @@ -356,34 +358,30 @@ static int bfs_exec_spawn(const struct bfs_exec *execbuf) { pid_t pid = -1; int error; - struct bfs_spawn ctx; - if (bfs_spawn_init(&ctx) != 0) { + struct bfs_spawn spawn; + if (bfs_spawn_init(&spawn) != 0) { return -1; } - if (bfs_spawn_setflags(&ctx, BFS_SPAWN_USEPATH) != 0) { + if (bfs_spawn_setflags(&spawn, BFS_SPAWN_USEPATH) != 0) { goto fail; } // Reset RLIMIT_NOFILE, to avoid breaking applications that use select() - struct rlimit rl = { - .rlim_cur = execbuf->ctx->nofile_soft, - .rlim_max = execbuf->ctx->nofile_hard, - }; - if (bfs_spawn_addsetrlimit(&ctx, RLIMIT_NOFILE, &rl) != 0) { + if (bfs_spawn_addsetrlimit(&spawn, RLIMIT_NOFILE, &ctx->orig_nofile) != 0) { goto fail; } if (execbuf->wd_fd >= 0) { - if (bfs_spawn_addfchdir(&ctx, execbuf->wd_fd) != 0) { + if (bfs_spawn_addfchdir(&spawn, execbuf->wd_fd) != 0) { goto fail; } } - pid = bfs_spawn(execbuf->argv[0], &ctx, execbuf->argv, NULL); + pid = bfs_spawn(execbuf->argv[0], &spawn, execbuf->argv, NULL); fail: error = errno; - bfs_spawn_destroy(&ctx); + bfs_spawn_destroy(&spawn); if (pid < 0) { errno = error; return -1; @@ -409,9 +407,9 @@ fail: if (!str) { str = "unknown"; } - bfs_warning(execbuf->ctx, "Command '${ex}%s${rs}' terminated by signal %d (%s)\n", execbuf->argv[0], sig, str); + bfs_warning(ctx, "Command '${ex}%s${rs}' terminated by signal %d (%s)\n", execbuf->argv[0], sig, str); } else { - bfs_warning(execbuf->ctx, "Command '${ex}%s${rs}' terminated abnormally\n", execbuf->argv[0]); + bfs_warning(ctx, "Command '${ex}%s${rs}' terminated abnormally\n", execbuf->argv[0]); } errno = 0; -- cgit v1.2.3 From cdbd5e6fcf1f447b515e717f7d7194471ef714a7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 23 Nov 2023 13:35:57 -0500 Subject: ctx: Switch paths from darray to RESERVE() --- src/ctx.c | 5 ++--- src/ctx.h | 3 +++ src/eval.c | 3 +-- src/parse.c | 20 ++++++++++---------- 4 files changed, 16 insertions(+), 15 deletions(-) (limited to 'src/ctx.c') diff --git a/src/ctx.c b/src/ctx.c index 692f5c5..f5dc465 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -4,7 +4,6 @@ #include "ctx.h" #include "alloc.h" #include "color.h" -#include "darray.h" #include "diag.h" #include "expr.h" #include "mtab.h" @@ -242,10 +241,10 @@ int bfs_ctx_free(struct bfs_ctx *ctx) { free_colors(ctx->colors); - for (size_t i = 0; i < darray_length(ctx->paths); ++i) { + for (size_t i = 0; i < ctx->npaths; ++i) { free((char *)ctx->paths[i]); } - darray_free(ctx->paths); + free(ctx->paths); free(ctx->argv); free(ctx); diff --git a/src/ctx.h b/src/ctx.h index 1c7df63..75891da 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -27,6 +27,9 @@ struct bfs_ctx { /** The root paths. */ const char **paths; + /** The number of root paths. */ + size_t npaths; + /** The main command line expression. */ struct bfs_expr *expr; /** An expression for files to filter out. */ diff --git a/src/eval.c b/src/eval.c index eb4a0ca..55b14f0 100644 --- a/src/eval.c +++ b/src/eval.c @@ -12,7 +12,6 @@ #include "color.h" #include "config.h" #include "ctx.h" -#include "darray.h" #include "diag.h" #include "dir.h" #include "dstring.h" @@ -1622,7 +1621,7 @@ int bfs_eval(struct bfs_ctx *ctx) { struct bftw_args bftw_args = { .paths = ctx->paths, - .npaths = darray_length(ctx->paths), + .npaths = ctx->npaths, .callback = eval_callback, .ptr = &args, .nopenfd = fdlimit, diff --git a/src/parse.c b/src/parse.c index d3938fc..13e65bc 100644 --- a/src/parse.c +++ b/src/parse.c @@ -15,7 +15,6 @@ #include "color.h" #include "config.h" #include "ctx.h" -#include "darray.h" #include "diag.h" #include "dir.h" #include "eval.h" @@ -481,16 +480,17 @@ static char **parser_advance(struct parser_state *state, enum token_type type, s * Parse a root path. */ static int parse_root(struct parser_state *state, const char *path) { - char *copy = strdup(path); - if (!copy) { - parse_perror(state, "strdup()"); + struct bfs_ctx *ctx = state->ctx; + const char **root = RESERVE(const char *, &ctx->paths, &ctx->npaths); + if (!root) { + parse_perror(state, "RESERVE()"); return -1; } - struct bfs_ctx *ctx = state->ctx; - if (DARRAY_PUSH(&ctx->paths, ©) != 0) { - parse_perror(state, "DARRAY_PUSH()"); - free(copy); + *root = strdup(path); + if (!*root) { + --ctx->npaths; + parse_perror(state, "strdup()"); return -1; } @@ -3617,7 +3617,7 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) { } } - for (size_t i = 0; i < darray_length(ctx->paths); ++i) { + for (size_t i = 0; i < ctx->npaths; ++i) { const char *path = ctx->paths[i]; char c = path[0]; if (c == '-' || c == '(' || c == ')' || c == '!' || c == ',') { @@ -3778,7 +3778,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { goto fail; } - if (darray_length(ctx->paths) == 0 && state.implicit_root) { + if (ctx->npaths == 0 && state.implicit_root) { if (parse_root(&state, ".") != 0) { goto fail; } -- cgit v1.2.3 From 70092ae5c8f83a99fbc98dc8e2ca2eaab676a5a8 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 20 Dec 2023 20:18:35 -0500 Subject: expr: Arena-allocate expressions --- src/ctx.c | 11 +- src/ctx.h | 6 ++ src/expr.c | 22 ++-- src/expr.h | 19 +++- src/opt.c | 113 +++++++------------- src/parse.c | 340 +++++++++++++++++++++++------------------------------------- 6 files changed, 210 insertions(+), 301 deletions(-) (limited to 'src/ctx.c') diff --git a/src/ctx.c b/src/ctx.c index f5dc465..a3c5140 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -22,6 +22,9 @@ struct bfs_ctx *bfs_ctx_new(void) { return NULL; } + SLIST_INIT(&ctx->expr_list); + ARENA_INIT(&ctx->expr_arena, struct bfs_expr); + ctx->maxdepth = INT_MAX; ctx->flags = BFTW_RECOVER; ctx->strategy = BFTW_BFS; @@ -202,9 +205,6 @@ int bfs_ctx_free(struct bfs_ctx *ctx) { CFILE *cout = ctx->cout; CFILE *cerr = ctx->cerr; - bfs_expr_free(ctx->exclude); - bfs_expr_free(ctx->expr); - bfs_mtab_free(ctx->mtab); bfs_groups_free(ctx->groups); @@ -241,6 +241,11 @@ int bfs_ctx_free(struct bfs_ctx *ctx) { free_colors(ctx->colors); + for_slist (struct bfs_expr, expr, &ctx->expr_list) { + bfs_expr_clear(expr); + } + arena_destroy(&ctx->expr_arena); + for (size_t i = 0; i < ctx->npaths; ++i) { free((char *)ctx->paths[i]); } diff --git a/src/ctx.h b/src/ctx.h index 75891da..aa91f2c 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -8,9 +8,11 @@ #ifndef BFS_CTX_H #define BFS_CTX_H +#include "alloc.h" #include "bftw.h" #include "config.h" #include "diag.h" +#include "expr.h" #include "trie.h" #include #include @@ -34,6 +36,10 @@ struct bfs_ctx { struct bfs_expr *expr; /** An expression for files to filter out. */ struct bfs_expr *exclude; + /** A list of allocated expressions. */ + struct bfs_exprs expr_list; + /** bfs_expr arena. */ + struct arena expr_arena; /** -mindepth option. */ int mindepth; diff --git a/src/expr.c b/src/expr.c index 380038c..2002fc7 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3,24 +3,27 @@ #include "expr.h" #include "alloc.h" +#include "ctx.h" #include "eval.h" #include "exec.h" #include "printf.h" #include "xregex.h" #include #include +#include -struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { - struct bfs_expr *expr = ZALLOC(struct bfs_expr); +struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval_fn, size_t argc, char **argv) { + struct bfs_expr *expr = arena_alloc(&ctx->expr_arena); if (!expr) { - perror("zalloc()"); return NULL; } + memset(expr, 0, sizeof(*expr)); expr->eval_fn = eval_fn; expr->argc = argc; expr->argv = argv; expr->probability = 0.5; + SLIST_PREPEND(&ctx->expr_list, expr); return expr; } @@ -36,21 +39,12 @@ bool bfs_expr_never_returns(const struct bfs_expr *expr) { return expr->always_true && expr->always_false; } -void bfs_expr_free(struct bfs_expr *expr) { - if (!expr) { - return; - } - - if (bfs_expr_is_parent(expr)) { - bfs_expr_free(expr->rhs); - bfs_expr_free(expr->lhs); - } else if (expr->eval_fn == eval_exec) { +void bfs_expr_clear(struct bfs_expr *expr) { + if (expr->eval_fn == eval_exec) { bfs_exec_free(expr->exec); } else if (expr->eval_fn == eval_fprintf) { bfs_printf_free(expr->printf); } else if (expr->eval_fn == eval_regex) { bfs_regfree(expr->regex); } - - free(expr); } diff --git a/src/expr.h b/src/expr.h index 95118b9..290f1f8 100644 --- a/src/expr.h +++ b/src/expr.h @@ -74,10 +74,21 @@ enum bfs_size_unit { BFS_PB, }; +/** + * A linked list of expressions. + */ +struct bfs_exprs { + struct bfs_expr *head; + struct bfs_expr **tail; +}; + /** * A command line expression. */ struct bfs_expr { + /** The next allocated expression. */ + struct bfs_expr *next; + /** The function that evaluates this expression. */ bfs_eval_fn *eval_fn; @@ -199,10 +210,12 @@ struct bfs_expr { }; }; +struct bfs_ctx; + /** * Create a new expression. */ -struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval, size_t argc, char **argv); +struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval, size_t argc, char **argv); /** * @return Whether the expression has child expressions. @@ -220,8 +233,8 @@ bool bfs_expr_never_returns(const struct bfs_expr *expr); bool bfs_expr_cmp(const struct bfs_expr *expr, long long n); /** - * Free an expression tree. + * Free any resources owned by an expression. */ -void bfs_expr_free(struct bfs_expr *expr); +void bfs_expr_clear(struct bfs_expr *expr); #endif // BFS_EXPR_H diff --git a/src/opt.c b/src/opt.c index a989670..1dc985a 100644 --- a/src/opt.c +++ b/src/opt.c @@ -284,7 +284,7 @@ static void df_join(struct df_domain *dest, const struct df_domain *src) { */ struct bfs_opt { /** The context we're optimizing. */ - const struct bfs_ctx *ctx; + struct bfs_ctx *ctx; /** Data flow state before this expression is evaluated. */ struct df_domain before; @@ -330,31 +330,22 @@ static void opt_warning(const struct bfs_opt *opt, const struct bfs_expr *expr, } /** Create a constant expression. */ -static struct bfs_expr *opt_const(bool value) { +static struct bfs_expr *opt_const(const struct bfs_opt *opt, bool value) { static bfs_eval_fn *fns[] = {eval_false, eval_true}; static char *fake_args[] = {"-false", "-true"}; - return bfs_expr_new(fns[value], 1, &fake_args[value]); -} - -/** Extract a child expression, freeing the outer expression. */ -static struct bfs_expr *extract_child_expr(struct bfs_expr *expr, struct bfs_expr **child) { - struct bfs_expr *ret = *child; - *child = NULL; - bfs_expr_free(expr); - return ret; + return bfs_expr_new(opt->ctx, fns[value], 1, &fake_args[value]); } /** * Negate an expression. */ -static struct bfs_expr *negate_expr(struct bfs_expr *rhs, char **argv) { +static struct bfs_expr *negate_expr(const struct bfs_opt *opt, struct bfs_expr *rhs, char **argv) { if (rhs->eval_fn == eval_not) { - return extract_child_expr(rhs, &rhs->rhs); + return rhs->rhs; } - struct bfs_expr *expr = bfs_expr_new(eval_not, 1, argv); + struct bfs_expr *expr = bfs_expr_new(opt->ctx, eval_not, 1, argv); if (!expr) { - bfs_expr_free(rhs); return NULL; } @@ -373,7 +364,7 @@ static struct bfs_expr *optimize_or_expr(const struct bfs_opt *opt, struct bfs_e static struct bfs_expr *de_morgan(const struct bfs_opt *opt, struct bfs_expr *expr, char **argv) { bool debug = opt_debug(opt, 1, "De Morgan's laws: %pe ", expr); - struct bfs_expr *parent = negate_expr(expr, argv); + struct bfs_expr *parent = negate_expr(opt, expr, argv); if (!parent) { return NULL; } @@ -393,10 +384,9 @@ static struct bfs_expr *de_morgan(const struct bfs_opt *opt, struct bfs_expr *ex expr->argv = &fake_and_arg; } - expr->lhs = negate_expr(expr->lhs, argv); - expr->rhs = negate_expr(expr->rhs, argv); + expr->lhs = negate_expr(opt, expr->lhs, argv); + expr->rhs = negate_expr(opt, expr->rhs, argv); if (!expr->lhs || !expr->rhs) { - bfs_expr_free(parent); return NULL; } @@ -411,7 +401,6 @@ static struct bfs_expr *de_morgan(const struct bfs_opt *opt, struct bfs_expr *ex expr->rhs = optimize_not_expr(opt, expr->rhs); } if (!expr->lhs || !expr->rhs) { - bfs_expr_free(parent); return NULL; } @@ -426,7 +415,6 @@ static struct bfs_expr *de_morgan(const struct bfs_opt *opt, struct bfs_expr *ex parent = expr; } if (!expr) { - bfs_expr_free(parent); return NULL; } @@ -450,16 +438,15 @@ static struct bfs_expr *optimize_not_expr(const struct bfs_opt *opt, struct bfs_ int optlevel = opt->ctx->optlevel; if (optlevel >= 1) { if (rhs->eval_fn == eval_true || rhs->eval_fn == eval_false) { - struct bfs_expr *ret = opt_const(rhs->eval_fn == eval_false); + struct bfs_expr *ret = opt_const(opt, rhs->eval_fn == eval_false); opt_debug(opt, 1, "constant propagation: %pe <==> %pe\n", expr, ret); - bfs_expr_free(expr); return ret; } else if (rhs->eval_fn == eval_not) { opt_debug(opt, 1, "double negation: %pe <==> %pe\n", expr, rhs->rhs); - return extract_child_expr(expr, &rhs->rhs); + return rhs->rhs; } else if (bfs_expr_never_returns(rhs)) { opt_debug(opt, 1, "reachability: %pe <==> %pe\n", expr, rhs); - return extract_child_expr(expr, &expr->rhs); + return expr->rhs; } else if ((rhs->eval_fn == eval_and || rhs->eval_fn == eval_or) && (rhs->lhs->eval_fn == eval_not || rhs->rhs->eval_fn == eval_not)) { return de_morgan(opt, expr, expr->argv); @@ -480,17 +467,13 @@ static struct bfs_expr *optimize_not_expr_recursive(struct bfs_opt *opt, struct struct bfs_opt rhs_state = *opt; expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); if (!expr->rhs) { - goto fail; + return NULL; } opt->after_true = rhs_state.after_false; opt->after_false = rhs_state.after_true; return optimize_not_expr(opt, expr); - -fail: - bfs_expr_free(expr); - return NULL; } /** Optimize a conjunction. */ @@ -505,18 +488,18 @@ static struct bfs_expr *optimize_and_expr(const struct bfs_opt *opt, struct bfs_ if (optlevel >= 1) { if (lhs->eval_fn == eval_true) { opt_debug(opt, 1, "conjunction elimination: %pe <==> %pe\n", expr, rhs); - return extract_child_expr(expr, &expr->rhs); + return expr->rhs; } else if (rhs->eval_fn == eval_true) { opt_debug(opt, 1, "conjunction elimination: %pe <==> %pe\n", expr, lhs); - return extract_child_expr(expr, &expr->lhs); + return expr->lhs; } else if (lhs->always_false) { opt_debug(opt, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); - return extract_child_expr(expr, &expr->lhs); + return expr->lhs; } else if (lhs->always_true && rhs->eval_fn == eval_false) { bool debug = opt_debug(opt, 1, "strength reduction: %pe <==> ", expr); - struct bfs_expr *ret = extract_child_expr(expr, &expr->lhs); - ret = negate_expr(ret, &fake_not_arg); + struct bfs_expr *ret = expr->lhs; + ret = negate_expr(opt, ret, &fake_not_arg); if (debug && ret) { cfprintf(ctx->cerr, "%pe\n", ret); } @@ -524,7 +507,7 @@ static struct bfs_expr *optimize_and_expr(const struct bfs_opt *opt, struct bfs_ } else if (optlevel >= 2 && lhs->pure && rhs->eval_fn == eval_false) { opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); - return extract_child_expr(expr, &expr->rhs); + return expr->rhs; } else if (lhs->eval_fn == eval_not && rhs->eval_fn == eval_not) { return de_morgan(opt, expr, expr->lhs->argv); } @@ -544,14 +527,14 @@ static struct bfs_expr *optimize_and_expr_recursive(struct bfs_opt *opt, struct struct bfs_opt lhs_state = *opt; expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); if (!expr->lhs) { - goto fail; + return NULL; } struct bfs_opt rhs_state = *opt; rhs_state.before = lhs_state.after_true; expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); if (!expr->rhs) { - goto fail; + return NULL; } opt->after_true = rhs_state.after_true; @@ -559,10 +542,6 @@ static struct bfs_expr *optimize_and_expr_recursive(struct bfs_opt *opt, struct df_join(&opt->after_false, &rhs_state.after_false); return optimize_and_expr(opt, expr); - -fail: - bfs_expr_free(expr); - return NULL; } /** Optimize a disjunction. */ @@ -578,17 +557,17 @@ static struct bfs_expr *optimize_or_expr(const struct bfs_opt *opt, struct bfs_e if (lhs->always_true) { opt_debug(opt, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); - return extract_child_expr(expr, &expr->lhs); + return expr->lhs; } else if (lhs->eval_fn == eval_false) { opt_debug(opt, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, rhs); - return extract_child_expr(expr, &expr->rhs); + return expr->rhs; } else if (rhs->eval_fn == eval_false) { opt_debug(opt, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, lhs); - return extract_child_expr(expr, &expr->lhs); + return expr->lhs; } else if (lhs->always_false && rhs->eval_fn == eval_true) { bool debug = opt_debug(opt, 1, "strength reduction: %pe <==> ", expr); - struct bfs_expr *ret = extract_child_expr(expr, &expr->lhs); - ret = negate_expr(ret, &fake_not_arg); + struct bfs_expr *ret = expr->lhs; + ret = negate_expr(opt, ret, &fake_not_arg); if (debug && ret) { cfprintf(ctx->cerr, "%pe\n", ret); } @@ -596,7 +575,7 @@ static struct bfs_expr *optimize_or_expr(const struct bfs_opt *opt, struct bfs_e } else if (optlevel >= 2 && lhs->pure && rhs->eval_fn == eval_true) { opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); - return extract_child_expr(expr, &expr->rhs); + return expr->rhs; } else if (lhs->eval_fn == eval_not && rhs->eval_fn == eval_not) { return de_morgan(opt, expr, expr->lhs->argv); } @@ -616,14 +595,14 @@ static struct bfs_expr *optimize_or_expr_recursive(struct bfs_opt *opt, struct b struct bfs_opt lhs_state = *opt; expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); if (!expr->lhs) { - goto fail; + return NULL; } struct bfs_opt rhs_state = *opt; rhs_state.before = lhs_state.after_false; expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); if (!expr->rhs) { - goto fail; + return NULL; } opt->after_false = rhs_state.after_false; @@ -631,10 +610,6 @@ static struct bfs_expr *optimize_or_expr_recursive(struct bfs_opt *opt, struct b df_join(&opt->after_true, &rhs_state.after_true); return optimize_or_expr(opt, expr); - -fail: - bfs_expr_free(expr); - return NULL; } /** Optimize an expression in an ignored-result context. */ @@ -646,23 +621,22 @@ static struct bfs_expr *ignore_result(const struct bfs_opt *opt, struct bfs_expr if (expr->eval_fn == eval_not) { opt_debug(opt, 1, "ignored result: %pe --> %pe\n", expr, expr->rhs); opt_warning(opt, expr, "The result of this expression is ignored.\n\n"); - expr = extract_child_expr(expr, &expr->rhs); + expr = expr->rhs; } else if (optlevel >= 2 && (expr->eval_fn == eval_and || expr->eval_fn == eval_or || expr->eval_fn == eval_comma) && expr->rhs->pure) { opt_debug(opt, 2, "ignored result: %pe --> %pe\n", expr, expr->lhs); opt_warning(opt, expr->rhs, "The result of this expression is ignored.\n\n"); - expr = extract_child_expr(expr, &expr->lhs); + expr = expr->lhs; } else { break; } } if (optlevel >= 2 && expr->pure && expr->eval_fn != eval_false) { - struct bfs_expr *ret = opt_const(false); + struct bfs_expr *ret = opt_const(opt, false); opt_debug(opt, 2, "ignored result: %pe --> %pe\n", expr, ret); opt_warning(opt, expr, "The result of this expression is ignored.\n\n"); - bfs_expr_free(expr); return ret; } } @@ -684,15 +658,15 @@ static struct bfs_expr *optimize_comma_expr(const struct bfs_opt *opt, struct bf if (bfs_expr_never_returns(lhs)) { opt_debug(opt, 1, "reachability: %pe <==> %pe\n", expr, lhs); opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); - return extract_child_expr(expr, &expr->lhs); + return expr->lhs; } else if ((lhs->always_true && rhs->eval_fn == eval_true) || (lhs->always_false && rhs->eval_fn == eval_false)) { opt_debug(opt, 1, "redundancy elimination: %pe <==> %pe\n", expr, lhs); - return extract_child_expr(expr, &expr->lhs); + return expr->lhs; } else if (optlevel >= 2 && lhs->pure) { opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); - return extract_child_expr(expr, &expr->rhs); + return expr->rhs; } } @@ -710,7 +684,7 @@ static struct bfs_expr *optimize_comma_expr_recursive(struct bfs_opt *opt, struc struct bfs_opt lhs_state = *opt; expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); if (!expr->lhs) { - goto fail; + return NULL; } struct bfs_opt rhs_state = *opt; @@ -719,14 +693,10 @@ static struct bfs_expr *optimize_comma_expr_recursive(struct bfs_opt *opt, struc expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); if (!expr->rhs) { - goto fail; + return NULL; } return optimize_comma_expr(opt, expr); - -fail: - bfs_expr_free(expr); - return NULL; } /** Optimize an icmp-style ([+-]N) expression. */ @@ -1213,10 +1183,9 @@ static struct bfs_expr *optimize_expr_recursive(struct bfs_opt *opt, struct bfs_ opt->after_false = opt->before; if (optlevel >= 2 && df_is_bottom(&opt->before)) { - struct bfs_expr *ret = opt_const(false); + struct bfs_expr *ret = opt_const(opt, false); opt_debug(opt, 2, "reachability: %pe --> %pe\n", expr, ret); opt_warning(opt, expr, "This expression is unreachable.\n\n"); - bfs_expr_free(expr); return ret; } @@ -1257,10 +1226,9 @@ static struct bfs_expr *optimize_expr_recursive(struct bfs_opt *opt, struct bfs_ if (df_is_bottom(&opt->after_true)) { if (expr->pure) { - struct bfs_expr *ret = opt_const(false); + struct bfs_expr *ret = opt_const(opt, false); opt_warning(opt, expr, "This expression is always false.\n\n"); opt_debug(opt, 2, "data flow: %pe --> %pe\n", expr, ret); - bfs_expr_free(expr); return ret; } else { expr->always_false = true; @@ -1268,10 +1236,9 @@ static struct bfs_expr *optimize_expr_recursive(struct bfs_opt *opt, struct bfs_ } } else if (df_is_bottom(&opt->after_false)) { if (expr->pure) { - struct bfs_expr *ret = opt_const(true); + struct bfs_expr *ret = opt_const(opt, true); opt_debug(opt, 2, "data flow: %pe --> %pe\n", expr, ret); opt_warning(opt, expr, "This expression is always true.\n\n"); - bfs_expr_free(expr); return ret; } else { expr->always_true = true; diff --git a/src/parse.c b/src/parse.c index 18a1064..17fe8ad 100644 --- a/src/parse.c +++ b/src/parse.c @@ -54,50 +54,6 @@ static char *fake_or_arg = "-o"; static char *fake_print_arg = "-print"; static char *fake_true_arg = "-true"; -/** - * Create a new unary expression. - */ -static struct bfs_expr *new_unary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *rhs, char **argv) { - struct bfs_expr *expr = bfs_expr_new(eval_fn, 1, argv); - if (!expr) { - bfs_expr_free(rhs); - return NULL; - } - - expr->lhs = NULL; - expr->rhs = rhs; - bfs_assert(bfs_expr_is_parent(expr)); - - expr->persistent_fds = rhs->persistent_fds; - expr->ephemeral_fds = rhs->ephemeral_fds; - return expr; -} - -/** - * Create a new binary expression. - */ -static struct bfs_expr *new_binary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *lhs, struct bfs_expr *rhs, char **argv) { - struct bfs_expr *expr = bfs_expr_new(eval_fn, 1, argv); - if (!expr) { - bfs_expr_free(rhs); - bfs_expr_free(lhs); - return NULL; - } - - expr->lhs = lhs; - expr->rhs = rhs; - bfs_assert(bfs_expr_is_parent(expr)); - - expr->persistent_fds = lhs->persistent_fds + rhs->persistent_fds; - if (lhs->ephemeral_fds > rhs->ephemeral_fds) { - expr->ephemeral_fds = lhs->ephemeral_fds; - } else { - expr->ephemeral_fds = rhs->ephemeral_fds; - } - - return expr; -} - /** * Color use flags. */ @@ -343,6 +299,58 @@ static bool parse_expr_warning(const struct bfs_parser *parser, const struct bfs return ret; } +/** + * Allocate a new expression. + */ +static struct bfs_expr *parse_new_expr(const struct bfs_parser *parser, bfs_eval_fn *eval_fn, size_t argc, char **argv) { + struct bfs_expr *expr = bfs_expr_new(parser->ctx, eval_fn, argc, argv); + if (!expr) { + parse_perror(parser, "bfs_expr_new()"); + } + return expr; +} + +/** + * Create a new unary expression. + */ +static struct bfs_expr *new_unary_expr(const struct bfs_parser *parser, bfs_eval_fn *eval_fn, struct bfs_expr *rhs, char **argv) { + struct bfs_expr *expr = parse_new_expr(parser, eval_fn, 1, argv); + if (!expr) { + return NULL; + } + + expr->lhs = NULL; + expr->rhs = rhs; + bfs_assert(bfs_expr_is_parent(expr)); + + expr->persistent_fds = rhs->persistent_fds; + expr->ephemeral_fds = rhs->ephemeral_fds; + return expr; +} + +/** + * Create a new binary expression. + */ +static struct bfs_expr *new_binary_expr(const struct bfs_parser *parser, bfs_eval_fn *eval_fn, struct bfs_expr *lhs, struct bfs_expr *rhs, char **argv) { + struct bfs_expr *expr = parse_new_expr(parser, eval_fn, 1, argv); + if (!expr) { + return NULL; + } + + expr->lhs = lhs; + expr->rhs = rhs; + bfs_assert(bfs_expr_is_parent(expr)); + + expr->persistent_fds = lhs->persistent_fds + rhs->persistent_fds; + if (lhs->ephemeral_fds > rhs->ephemeral_fds) { + expr->ephemeral_fds = lhs->ephemeral_fds; + } else { + expr->ephemeral_fds = rhs->ephemeral_fds; + } + + return expr; +} + /** * Fill in a "-print"-type expression. */ @@ -645,7 +653,7 @@ static bool looks_like_icmp(const char *str) { */ static struct bfs_expr *parse_flag(struct bfs_parser *parser, size_t argc) { char **argv = parser_advance(parser, T_FLAG, argc); - return bfs_expr_new(eval_true, argc, argv); + return parse_new_expr(parser, eval_true, argc, argv); } /** @@ -674,7 +682,7 @@ static struct bfs_expr *parse_unary_flag(struct bfs_parser *parser) { */ static struct bfs_expr *parse_option(struct bfs_parser *parser, size_t argc) { char **argv = parser_advance(parser, T_OPTION, argc); - return bfs_expr_new(eval_true, argc, argv); + return parse_new_expr(parser, eval_true, argc, argv); } /** @@ -703,7 +711,7 @@ static struct bfs_expr *parse_unary_option(struct bfs_parser *parser) { */ static struct bfs_expr *parse_test(struct bfs_parser *parser, bfs_eval_fn *eval_fn, size_t argc) { char **argv = parser_advance(parser, T_TEST, argc); - return bfs_expr_new(eval_fn, argc, argv); + return parse_new_expr(parser, eval_fn, argc, argv); } /** @@ -742,7 +750,7 @@ static struct bfs_expr *parse_action(struct bfs_parser *parser, bfs_eval_fn *eva parser->implicit_print = false; } - return bfs_expr_new(eval_fn, argc, argv); + return parse_new_expr(parser, eval_fn, argc, argv); } /** @@ -771,7 +779,7 @@ static struct bfs_expr *parse_unary_action(struct bfs_parser *parser, bfs_eval_f */ static int parse_exclude(struct bfs_parser *parser, struct bfs_expr *expr) { struct bfs_ctx *ctx = parser->ctx; - ctx->exclude = new_binary_expr(eval_or, ctx->exclude, expr, &fake_or_arg); + ctx->exclude = new_binary_expr(parser, eval_or, ctx->exclude, expr, &fake_or_arg); if (ctx->exclude) { return 0; } else { @@ -789,7 +797,6 @@ static struct bfs_expr *parse_test_icmp(struct bfs_parser *parser, bfs_eval_fn * } if (!parse_icmp(parser, expr, 0)) { - bfs_expr_free(expr); return NULL; } @@ -848,7 +855,6 @@ static struct bfs_expr *parse_debug(struct bfs_parser *parser, int arg1, int arg if (parse_debug_flag(flag, len, "help")) { debug_help(ctx->cout); parser->just_info = true; - bfs_expr_free(expr); return NULL; } else if (parse_debug_flag(flag, len, "all")) { ctx->debug = DEBUG_ALL; @@ -896,7 +902,6 @@ static struct bfs_expr *parse_optlevel(struct bfs_parser *parser, int arg1, int if (strcmp(expr->argv[0], "-Ofast") == 0) { *optlevel = 4; } else if (!parse_int(parser, expr->argv, expr->argv[0] + 2, optlevel, IF_INT | IF_UNSIGNED)) { - bfs_expr_free(expr); return NULL; } @@ -963,16 +968,12 @@ static struct bfs_expr *parse_newer(struct bfs_parser *parser, int field, int ar struct bfs_stat sb; if (stat_arg(parser, &expr->argv[1], &sb) != 0) { - goto fail; + return NULL; } expr->reftime = sb.mtime; expr->stat_field = field; return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1004,7 +1005,7 @@ static struct bfs_expr *parse_time(struct bfs_parser *parser, int field, int arg const char *tail = parse_icmp(parser, expr, IF_PARTIAL_OK); if (!tail) { - goto fail; + return NULL; } if (!*tail) { @@ -1033,7 +1034,7 @@ static struct bfs_expr *parse_time(struct bfs_parser *parser, int field, int arg break; default: parse_expr_error(parser, expr, "Unknown time unit ${bld}%c${rs}.\n", *tail); - goto fail; + return NULL; } expr->num += time; @@ -1044,20 +1045,16 @@ static struct bfs_expr *parse_time(struct bfs_parser *parser, int field, int arg tail = parse_int(parser, &expr->argv[1], tail, &time, IF_PARTIAL_OK | IF_LONG_LONG | IF_UNSIGNED); if (!tail) { - goto fail; + return NULL; } if (!*tail) { parse_expr_error(parser, expr, "Missing time unit.\n"); - goto fail; + return NULL; } } expr->time_unit = BFS_SECONDS; return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1087,7 +1084,6 @@ static struct bfs_expr *parse_color(struct bfs_parser *parser, int color, int ar if (color) { if (!colors) { parse_expr_error(parser, expr, "Error parsing $$LS_COLORS: %s.\n", xstrerror(ctx->colors_error)); - bfs_expr_free(expr); return NULL; } @@ -1182,7 +1178,6 @@ static struct bfs_expr *parse_depth_limit(struct bfs_parser *parser, int is_min, int *depth = is_min ? &ctx->mindepth : &ctx->maxdepth; char **arg = &expr->argv[1]; if (!parse_int(parser, arg, *arg, depth, IF_INT | IF_UNSIGNED)) { - bfs_expr_free(expr); return NULL; } @@ -1235,7 +1230,7 @@ static struct bfs_expr *parse_exec(struct bfs_parser *parser, int flags, int arg } else { parse_perror(parser, "strndup()"); } - goto fail; + return NULL; } path = strchr(path, ':'); @@ -1257,10 +1252,6 @@ static struct bfs_expr *parse_exec(struct bfs_parser *parser, int flags, int arg } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1292,7 +1283,6 @@ static struct bfs_expr *parse_f(struct bfs_parser *parser, int arg1, int arg2) { } if (parse_root(parser, expr->argv[1]) != 0) { - bfs_expr_free(expr); return NULL; } @@ -1318,7 +1308,7 @@ static struct bfs_expr *parse_files0_from(struct bfs_parser *parser, int arg1, i } if (!file) { parse_expr_error(parser, expr, "%m.\n"); - goto fail; + return NULL; } while (true) { @@ -1348,10 +1338,9 @@ static struct bfs_expr *parse_files0_from(struct bfs_parser *parser, int arg1, i return expr; fail: - if (file && file != stdin) { + if (file != stdin) { fclose(file); } - bfs_expr_free(expr); return NULL; } @@ -1385,7 +1374,6 @@ static struct bfs_expr *parse_flags(struct bfs_parser *parser, int arg1, int arg } else { parse_expr_error(parser, expr, "Invalid flags.\n"); } - bfs_expr_free(expr); return NULL; } @@ -1398,18 +1386,14 @@ static struct bfs_expr *parse_flags(struct bfs_parser *parser, int arg1, int arg static struct bfs_expr *parse_fls(struct bfs_parser *parser, int arg1, int arg2) { struct bfs_expr *expr = parse_unary_action(parser, eval_fls); if (!expr) { - goto fail; + return NULL; } if (expr_open(parser, expr, expr->argv[1]) != 0) { - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1417,16 +1401,15 @@ fail: */ static struct bfs_expr *parse_fprint(struct bfs_parser *parser, int arg1, int arg2) { struct bfs_expr *expr = parse_unary_action(parser, eval_fprint); - if (expr) { - if (expr_open(parser, expr, expr->argv[1]) != 0) { - goto fail; - } + if (!expr) { + return NULL; } - return expr; -fail: - bfs_expr_free(expr); - return NULL; + if (expr_open(parser, expr, expr->argv[1]) != 0) { + return NULL; + } + + return expr; } /** @@ -1434,16 +1417,15 @@ fail: */ static struct bfs_expr *parse_fprint0(struct bfs_parser *parser, int arg1, int arg2) { struct bfs_expr *expr = parse_unary_action(parser, eval_fprint0); - if (expr) { - if (expr_open(parser, expr, expr->argv[1]) != 0) { - goto fail; - } + if (!expr) { + return NULL; } - return expr; -fail: - bfs_expr_free(expr); - return NULL; + if (expr_open(parser, expr, expr->argv[1]) != 0) { + return NULL; + } + + return expr; } /** @@ -1470,18 +1452,14 @@ static struct bfs_expr *parse_fprintf(struct bfs_parser *parser, int arg1, int a } if (expr_open(parser, expr, file) != 0) { - goto fail; + return NULL; } if (bfs_printf_parse(parser->ctx, expr, format) != 0) { - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1495,7 +1473,6 @@ static struct bfs_expr *parse_fstype(struct bfs_parser *parser, int arg1, int ar if (!bfs_ctx_mtab(parser->ctx)) { parse_expr_error(parser, expr, "Couldn't parse the mount table: %m.\n"); - bfs_expr_free(expr); return NULL; } @@ -1517,21 +1494,17 @@ static struct bfs_expr *parse_group(struct bfs_parser *parser, int arg1, int arg expr->int_cmp = BFS_INT_EQUAL; } else if (looks_like_icmp(expr->argv[1])) { if (!parse_icmp(parser, expr, 0)) { - goto fail; + return NULL; } } else if (errno) { parse_expr_error(parser, expr, "%m.\n"); - goto fail; + return NULL; } else { parse_expr_error(parser, expr, "No such group.\n"); - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1564,21 +1537,17 @@ static struct bfs_expr *parse_user(struct bfs_parser *parser, int arg1, int arg2 expr->int_cmp = BFS_INT_EQUAL; } else if (looks_like_icmp(expr->argv[1])) { if (!parse_icmp(parser, expr, 0)) { - goto fail; + return NULL; } } else if (errno) { parse_expr_error(parser, expr, "%m.\n"); - goto fail; + return NULL; } else { parse_expr_error(parser, expr, "No such user.\n"); - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1614,13 +1583,11 @@ static struct bfs_expr *parse_jobs(struct bfs_parser *parser, int arg1, int arg2 unsigned int n; if (!parse_int(parser, expr->argv, expr->argv[0] + 2, &n, IF_INT | IF_UNSIGNED)) { - bfs_expr_free(expr); return NULL; } if (n == 0) { parse_expr_error(parser, expr, "${bld}0${rs} is not enough threads.\n"); - bfs_expr_free(expr); return NULL; } @@ -1680,7 +1647,6 @@ static struct bfs_expr *parse_fnmatch(const struct bfs_parser *parser, struct bf expr->fnm_flags = FNM_CASEFOLD; #else parse_expr_error(parser, expr, "Missing platform support.\n"); - bfs_expr_free(expr); return NULL; #endif } else { @@ -1811,7 +1777,7 @@ static struct bfs_expr *parse_newerxy(struct bfs_parser *parser, int arg1, int a struct bfs_expr *expr = parse_unary_test(parser, eval_newer); if (!expr) { - goto fail; + return NULL; } expr->stat_field = parse_newerxy_field(arg[6]); @@ -1819,12 +1785,12 @@ static struct bfs_expr *parse_newerxy(struct bfs_parser *parser, int arg1, int a parse_expr_error(parser, expr, "For ${blu}-newer${bld}XY${rs}, ${bld}X${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, or ${bld}B${rs}, not ${err}%c${rs}.\n", arg[6]); - goto fail; + return NULL; } if (arg[7] == 't') { if (parse_reftime(parser, expr) != 0) { - goto fail; + return NULL; } } else { enum bfs_stat_field field = parse_newerxy_field(arg[7]); @@ -1832,28 +1798,24 @@ static struct bfs_expr *parse_newerxy(struct bfs_parser *parser, int arg1, int a parse_expr_error(parser, expr, "For ${blu}-newer${bld}XY${rs}, ${bld}Y${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, ${bld}B${rs}, or ${bld}t${rs}, not ${err}%c${rs}.\n", arg[7]); - goto fail; + return NULL; } struct bfs_stat sb; if (stat_arg(parser, &expr->argv[1], &sb) != 0) { - goto fail; + return NULL; } const struct timespec *reftime = bfs_stat_time(&sb, field); if (!reftime) { parse_expr_error(parser, expr, "Couldn't get file %s.\n", bfs_stat_field_name(field)); - goto fail; + return NULL; } expr->reftime = *reftime; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1872,7 +1834,7 @@ static struct bfs_expr *parse_nogroup(struct bfs_parser *parser, int arg1, int a * Parse -nohidden. */ static struct bfs_expr *parse_nohidden(struct bfs_parser *parser, int arg1, int arg2) { - struct bfs_expr *hidden = bfs_expr_new(eval_hidden, 1, &fake_hidden_arg); + struct bfs_expr *hidden = parse_new_expr(parser, eval_hidden, 1, &fake_hidden_arg); if (!hidden) { return NULL; } @@ -2158,14 +2120,10 @@ static struct bfs_expr *parse_perm(struct bfs_parser *parser, int field, int arg } if (parse_mode(parser, mode, expr) != 0) { - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -2202,7 +2160,6 @@ static struct bfs_expr *parse_printf(struct bfs_parser *parser, int arg1, int ar init_print_expr(parser, expr); if (bfs_printf_parse(parser->ctx, expr, expr->argv[1]) != 0) { - bfs_expr_free(expr); return NULL; } @@ -2241,31 +2198,26 @@ static struct bfs_expr *parse_quit(struct bfs_parser *parser, int arg1, int arg2 static struct bfs_expr *parse_regex(struct bfs_parser *parser, int flags, int arg2) { struct bfs_expr *expr = parse_unary_test(parser, eval_regex); if (!expr) { - goto fail; + return NULL; } if (bfs_regcomp(&expr->regex, expr->argv[1], parser->regex_type, flags) != 0) { - if (!expr->regex) { + if (expr->regex) { + char *str = bfs_regerror(expr->regex); + if (str) { + parse_expr_error(parser, expr, "%s.\n", str); + free(str); + } else { + parse_perror(parser, "bfs_regerror()"); + } + } else { parse_perror(parser, "bfs_regcomp()"); - goto fail; } - char *str = bfs_regerror(expr->regex); - if (!str) { - parse_perror(parser, "bfs_regerror()"); - goto fail; - } - - parse_expr_error(parser, expr, "%s.\n", str); - free(str); - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -2324,8 +2276,6 @@ list_types: cfprintf(cfile, " ${bld}grep${rs}: Like ${grn}grep${rs}\n"); #endif cfprintf(cfile, " ${bld}sed${rs}: Like ${grn}sed${rs} (same as ${bld}posix-basic${rs})\n"); - - bfs_expr_free(expr); return NULL; } @@ -2348,7 +2298,6 @@ static struct bfs_expr *parse_samefile(struct bfs_parser *parser, int arg1, int struct bfs_stat sb; if (stat_arg(parser, &expr->argv[1], &sb) != 0) { - bfs_expr_free(expr); return NULL; } @@ -2396,8 +2345,6 @@ list_strategies: cfprintf(cfile, " ${bld}dfs${rs}: depth-first search\n"); cfprintf(cfile, " ${bld}ids${rs}: iterative deepening search\n"); cfprintf(cfile, " ${bld}eds${rs}: exponential deepening search\n"); - - bfs_expr_free(expr); return NULL; } @@ -2411,15 +2358,11 @@ static struct bfs_expr *parse_since(struct bfs_parser *parser, int field, int ar } if (parse_reftime(parser, expr) != 0) { - goto fail; + return NULL; } expr->stat_field = field; return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -2433,7 +2376,7 @@ static struct bfs_expr *parse_size(struct bfs_parser *parser, int arg1, int arg2 const char *unit = parse_icmp(parser, expr, IF_PARTIAL_OK); if (!unit) { - goto fail; + return NULL; } if (strlen(unit) > 1) { @@ -2468,16 +2411,12 @@ static struct bfs_expr *parse_size(struct bfs_parser *parser, int arg1, int arg2 break; default: - goto bad_unit; + bad_unit: + parse_expr_error(parser, expr, "Expected a size unit (one of ${bld}cwbkMGTP${rs}); found ${err}%pq${rs}.\n", unit); + return NULL; } return expr; - -bad_unit: - parse_expr_error(parser, expr, "Expected a size unit (one of ${bld}cwbkMGTP${rs}); found ${err}%pq${rs}.\n", unit); -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -2543,11 +2482,11 @@ static struct bfs_expr *parse_type(struct bfs_parser *parser, int x, int arg2) { case '\0': parse_expr_error(parser, expr, "Expected a type flag.\n"); - goto fail; + return NULL; default: parse_expr_error(parser, expr, "Unknown type flag ${err}%c${rs}; expected one of [${bld}bcdpflsD${rs}].\n", *c); - goto fail; + return NULL; } ++c; @@ -2558,15 +2497,11 @@ static struct bfs_expr *parse_type(struct bfs_parser *parser, int x, int arg2) { continue; } else { parse_expr_error(parser, expr, "Types must be comma-separated.\n"); - goto fail; + return NULL; } } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -3244,14 +3179,12 @@ static struct bfs_expr *parse_factor(struct bfs_parser *parser) { } if (skip_paths(parser) != 0) { - bfs_expr_free(expr); return NULL; } arg = parser->argv[0]; if (!arg || strcmp(arg, ")") != 0) { parse_argv_error(parser, parser->last_arg, 1, "Expected a ${red})${rs}.\n"); - bfs_expr_free(expr); return NULL; } @@ -3277,7 +3210,7 @@ static struct bfs_expr *parse_factor(struct bfs_parser *parser) { return NULL; } - return bfs_expr_new(eval_true, parser->argv - argv, argv); + return parse_new_expr(parser, eval_true, parser->argv - argv, argv); } else if (strcmp(arg, "!") == 0 || strcmp(arg, "-not") == 0) { char **argv = parser_advance(parser, T_OPERATOR, 1); @@ -3286,7 +3219,7 @@ static struct bfs_expr *parse_factor(struct bfs_parser *parser) { return NULL; } - return new_unary_expr(eval_not, factor, argv); + return new_unary_expr(parser, eval_not, factor, argv); } else { return parse_primary(parser); } @@ -3303,7 +3236,6 @@ static struct bfs_expr *parse_term(struct bfs_parser *parser) { while (term) { if (skip_paths(parser) != 0) { - bfs_expr_free(term); return NULL; } @@ -3326,11 +3258,10 @@ static struct bfs_expr *parse_term(struct bfs_parser *parser) { struct bfs_expr *lhs = term; struct bfs_expr *rhs = parse_factor(parser); if (!rhs) { - bfs_expr_free(lhs); return NULL; } - term = new_binary_expr(eval_and, lhs, rhs, argv); + term = new_binary_expr(parser, eval_and, lhs, rhs, argv); } return term; @@ -3346,7 +3277,6 @@ static struct bfs_expr *parse_clause(struct bfs_parser *parser) { while (clause) { if (skip_paths(parser) != 0) { - bfs_expr_free(clause); return NULL; } @@ -3364,11 +3294,10 @@ static struct bfs_expr *parse_clause(struct bfs_parser *parser) { struct bfs_expr *lhs = clause; struct bfs_expr *rhs = parse_term(parser); if (!rhs) { - bfs_expr_free(lhs); return NULL; } - clause = new_binary_expr(eval_or, lhs, rhs, argv); + clause = new_binary_expr(parser, eval_or, lhs, rhs, argv); } return clause; @@ -3383,7 +3312,6 @@ static struct bfs_expr *parse_expr(struct bfs_parser *parser) { while (expr) { if (skip_paths(parser) != 0) { - bfs_expr_free(expr); return NULL; } @@ -3401,11 +3329,10 @@ static struct bfs_expr *parse_expr(struct bfs_parser *parser) { struct bfs_expr *lhs = expr; struct bfs_expr *rhs = parse_clause(parser); if (!rhs) { - bfs_expr_free(lhs); return NULL; } - expr = new_binary_expr(eval_comma, lhs, rhs, argv); + expr = new_binary_expr(parser, eval_comma, lhs, rhs, argv); } return expr; @@ -3423,7 +3350,7 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { if (parser->argv[0]) { expr = parse_expr(parser); } else { - expr = bfs_expr_new(eval_true, 1, &fake_true_arg); + expr = parse_new_expr(parser, eval_true, 1, &fake_true_arg); } if (!expr) { return NULL; @@ -3431,19 +3358,19 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { if (parser->argv[0]) { parse_error(parser, "Unexpected argument.\n"); - goto fail; + return NULL; } if (parser->implicit_print) { - struct bfs_expr *print = bfs_expr_new(eval_fprint, 1, &fake_print_arg); + struct bfs_expr *print = parse_new_expr(parser, eval_fprint, 1, &fake_print_arg); if (!print) { - goto fail; + return NULL; } init_print_expr(parser, print); - expr = new_binary_expr(eval_and, expr, print, &fake_and_arg); + expr = new_binary_expr(parser, eval_and, expr, print, &fake_and_arg); if (!expr) { - goto fail; + return NULL; } } @@ -3461,7 +3388,7 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { if (parser->interactive) { bfs_warning(parser->ctx, "Do you want to continue? "); if (ynprompt() == 0) { - goto fail; + return NULL; } } @@ -3472,14 +3399,10 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { parse_conflict_error(parser, parser->ok_expr->argv, parser->ok_expr->argc, parser->files0_stdin_arg, 2, "${blu}%s${rs} conflicts with ${blu}%s${rs} ${bld}%s${rs}.\n", parser->ok_expr->argv[0], parser->files0_stdin_arg[0], parser->files0_stdin_arg[1]); - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } static const char *bftw_strategy_name(enum bftw_strategy strategy) { @@ -3637,7 +3560,7 @@ static void dump_costs(const struct bfs_ctx *ctx) { struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { struct bfs_ctx *ctx = bfs_ctx_new(); if (!ctx) { - perror("bfs_new_ctx()"); + perror("bfs_ctx_new()"); goto fail; } @@ -3715,7 +3638,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { .now = ctx->now, }; - ctx->exclude = bfs_expr_new(eval_false, 1, &fake_false_arg); + ctx->exclude = parse_new_expr(&parser, eval_false, 1, &fake_false_arg); if (!ctx->exclude) { goto fail; } @@ -3734,6 +3657,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { } if (bfs_optimize(ctx) != 0) { + bfs_perror(ctx, "bfs_optimize()"); goto fail; } -- cgit v1.2.3 From 4a36bb92a5bbdc41965a6d2c6eae6cdca5983474 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sun, 7 Jan 2024 12:19:17 -0500 Subject: expr: Make expressions variadic Rather than only unary/binary expressions, we now support an arbitrary number of children. The optimizer has been re-written almost completely and now supports optimal reordering of longer expression chains, rather than just arm-swapping. Fixes #85. --- src/color.c | 39 +- src/ctx.c | 2 +- src/diag.c | 5 +- src/eval.c | 55 +- src/expr.c | 37 +- src/expr.h | 28 +- src/opt.c | 2468 +++++++++++++++++++++++++++++++++++++++-------------------- src/parse.c | 73 +- 8 files changed, 1768 insertions(+), 939 deletions(-) (limited to 'src/ctx.c') diff --git a/src/color.c b/src/color.c index 5247cbf..1f10c04 100644 --- a/src/color.c +++ b/src/color.c @@ -1109,7 +1109,11 @@ attr(printf(2, 3)) static int cbuff(CFILE *cfile, const char *format, ...); /** Dump a parsed expression tree, for debugging. */ -static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { +static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose, int depth) { + if (depth >= 2) { + return dstrcat(&cfile->buffer, "(...)"); + } + if (!expr) { return dstrcat(&cfile->buffer, "(null)"); } @@ -1118,13 +1122,7 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { return -1; } - const struct bfs_expr *lhs = NULL; - const struct bfs_expr *rhs = NULL; - if (bfs_expr_is_parent(expr)) { - lhs = expr->lhs; - rhs = expr->rhs; - if (cbuff(cfile, "${red}%pq${rs}", expr->argv[0]) < 0) { return -1; } @@ -1152,21 +1150,20 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { } } - if (lhs) { - if (dstrcat(&cfile->buffer, " ") != 0) { - return -1; - } - if (print_expr(cfile, lhs, verbose) != 0) { - return -1; - } - } - - if (rhs) { + int count = 0; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { if (dstrcat(&cfile->buffer, " ") != 0) { return -1; } - if (print_expr(cfile, rhs, verbose) != 0) { - return -1; + if (++count >= 3) { + if (dstrcat(&cfile->buffer, "...") != 0) { + return -1; + } + break; + } else { + if (print_expr(cfile, child, verbose, depth + 1) != 0) { + return -1; + } } } @@ -1276,12 +1273,12 @@ static int cvbuff(CFILE *cfile, const char *format, va_list args) { break; case 'e': - if (print_expr(cfile, va_arg(args, const struct bfs_expr *), false) != 0) { + if (print_expr(cfile, va_arg(args, const struct bfs_expr *), false, 0) != 0) { return -1; } break; case 'E': - if (print_expr(cfile, va_arg(args, const struct bfs_expr *), true) != 0) { + if (print_expr(cfile, va_arg(args, const struct bfs_expr *), true, 0) != 0) { return -1; } break; diff --git a/src/ctx.c b/src/ctx.c index a3c5140..1f3e72e 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -241,7 +241,7 @@ int bfs_ctx_free(struct bfs_ctx *ctx) { free_colors(ctx->colors); - for_slist (struct bfs_expr, expr, &ctx->expr_list) { + for_slist (struct bfs_expr, expr, &ctx->expr_list, freelist) { bfs_expr_clear(expr); } arena_destroy(&ctx->expr_arena); diff --git a/src/diag.c b/src/diag.c index bf2343d..efa7ebd 100644 --- a/src/diag.c +++ b/src/diag.c @@ -158,9 +158,8 @@ static bool highlight_expr_recursive(const struct bfs_ctx *ctx, const struct bfs } } - if (bfs_expr_is_parent(expr)) { - ret |= highlight_expr_recursive(ctx, expr->lhs, args); - ret |= highlight_expr_recursive(ctx, expr->rhs, args); + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + ret |= highlight_expr_recursive(ctx, child, args); } return ret; diff --git a/src/eval.c b/src/eval.c index 372fcf5..49c4aff 100644 --- a/src/eval.c +++ b/src/eval.c @@ -372,11 +372,10 @@ static int eval_exec_finish(const struct bfs_expr *expr, const struct bfs_ctx *c } ret = -1; } - } else if (bfs_expr_is_parent(expr)) { - if (expr->lhs && eval_exec_finish(expr->lhs, ctx) != 0) { - ret = -1; - } - if (expr->rhs && eval_exec_finish(expr->rhs, ctx) != 0) { + } + + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + if (eval_exec_finish(child, ctx) != 0) { ret = -1; } } @@ -1045,50 +1044,48 @@ static bool eval_expr(struct bfs_expr *expr, struct bfs_eval *state) { * Evaluate a negation. */ bool eval_not(const struct bfs_expr *expr, struct bfs_eval *state) { - return !eval_expr(expr->rhs, state); + return !eval_expr(bfs_expr_children(expr), state); } /** * Evaluate a conjunction. */ bool eval_and(const struct bfs_expr *expr, struct bfs_eval *state) { - if (!eval_expr(expr->lhs, state)) { - return false; - } - - if (state->quit) { - return false; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + if (!eval_expr(child, state) || state->quit) { + return false; + } } - return eval_expr(expr->rhs, state); + return true; } /** * Evaluate a disjunction. */ bool eval_or(const struct bfs_expr *expr, struct bfs_eval *state) { - if (eval_expr(expr->lhs, state)) { - return true; - } - - if (state->quit) { - return false; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + if (eval_expr(child, state) || state->quit) { + return true; + } } - return eval_expr(expr->rhs, state); + return false; } /** * Evaluate the comma operator. */ bool eval_comma(const struct bfs_expr *expr, struct bfs_eval *state) { - eval_expr(expr->lhs, state); - - if (state->quit) { - return false; + bool ret; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + ret = eval_expr(child, state); + if (state->quit) { + break; + } } - return eval_expr(expr->rhs, state); + return ret; } /** Update the status bar. */ @@ -1571,12 +1568,8 @@ static bool eval_must_buffer(const struct bfs_expr *expr) { return true; } - if (bfs_expr_is_parent(expr)) { - if (expr->lhs && eval_must_buffer(expr->lhs)) { - return true; - } - - if (expr->rhs && eval_must_buffer(expr->rhs)) { + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + if (eval_must_buffer(child)) { return true; } } diff --git a/src/expr.c b/src/expr.c index 2002fc7..3e0033f 100644 --- a/src/expr.c +++ b/src/expr.c @@ -23,7 +23,12 @@ struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval_fn, size_t expr->argc = argc; expr->argv = argv; expr->probability = 0.5; - SLIST_PREPEND(&ctx->expr_list, expr); + SLIST_PREPEND(&ctx->expr_list, expr, freelist); + + if (bfs_expr_is_parent(expr)) { + SLIST_INIT(&expr->children); + } + return expr; } @@ -34,6 +39,36 @@ bool bfs_expr_is_parent(const struct bfs_expr *expr) { || expr->eval_fn == eval_comma; } +struct bfs_expr *bfs_expr_children(const struct bfs_expr *expr) { + if (bfs_expr_is_parent(expr)) { + return expr->children.head; + } else { + return NULL; + } +} + +void bfs_expr_append(struct bfs_expr *expr, struct bfs_expr *child) { + bfs_assert(bfs_expr_is_parent(expr)); + + SLIST_APPEND(&expr->children, child); + + if (!child->pure) { + expr->pure = false; + } + + expr->persistent_fds += child->persistent_fds; + if (expr->ephemeral_fds < child->ephemeral_fds) { + expr->ephemeral_fds = child->ephemeral_fds; + } +} + +void bfs_expr_extend(struct bfs_expr *expr, struct bfs_exprs *children) { + while (!SLIST_EMPTY(children)) { + struct bfs_expr *child = SLIST_POP(children); + bfs_expr_append(expr, child); + } +} + bool bfs_expr_never_returns(const struct bfs_expr *expr) { // Expressions that never return are vacuously both always true and always false return expr->always_true && expr->always_false; diff --git a/src/expr.h b/src/expr.h index 290f1f8..4d607a4 100644 --- a/src/expr.h +++ b/src/expr.h @@ -86,8 +86,10 @@ struct bfs_exprs { * A command line expression. */ struct bfs_expr { - /** The next allocated expression. */ + /** This expression's next sibling, if any. */ struct bfs_expr *next; + /** The next allocated expression. */ + struct { struct bfs_expr *next; } freelist; /** The function that evaluates this expression. */ bfs_eval_fn *eval_fn; @@ -123,12 +125,7 @@ struct bfs_expr { /** Auxilliary data for the evaluation function. */ union { /** Child expressions. */ - struct { - /** The left hand side of the expression. */ - struct bfs_expr *lhs; - /** The right hand side of the expression. */ - struct bfs_expr *rhs; - }; + struct bfs_exprs children; /** Integer comparisons. */ struct { @@ -218,10 +215,25 @@ struct bfs_ctx; struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval, size_t argc, char **argv); /** - * @return Whether the expression has child expressions. + * @return Whether this type of expression has children. */ bool bfs_expr_is_parent(const struct bfs_expr *expr); +/** + * @return The first child of this expression, or NULL if it has none. + */ +struct bfs_expr *bfs_expr_children(const struct bfs_expr *expr); + +/** + * Add a child to an expression. + */ +void bfs_expr_append(struct bfs_expr *expr, struct bfs_expr *child); + +/** + * Add a list of children to an expression. + */ +void bfs_expr_extend(struct bfs_expr *expr, struct bfs_exprs *children); + /** * @return Whether expr is known to always quit. */ diff --git a/src/opt.c b/src/opt.c index 1dc985a..7203c61 100644 --- a/src/opt.c +++ b/src/opt.c @@ -21,11 +21,12 @@ * -bar is likely to return false. * * -O4/-Ofast: aggressive optimizations that may affect correctness in corner - * cases. The main effect is to use impure to determine if any side-effects are - * reachable at all, and skipping the traversal if not. + * cases. The main effect is to use opt->impure to determine if any side- + * effects are reachable at all, skipping the traversal if not. */ #include "opt.h" +#include "bit.h" #include "color.h" #include "config.h" #include "ctx.h" @@ -33,6 +34,7 @@ #include "eval.h" #include "exec.h" #include "expr.h" +#include "list.h" #include "pwcache.h" #include #include @@ -41,9 +43,9 @@ #include #include -static char *fake_and_arg = "-a"; -static char *fake_or_arg = "-o"; -static char *fake_not_arg = "!"; +static char *fake_and_arg = "-and"; +static char *fake_or_arg = "-or"; +static char *fake_not_arg = "-not"; /** * The data flow domain for predicates. @@ -69,6 +71,70 @@ static void pred_join(enum df_pred *dest, enum df_pred src) { *dest |= src; } +/** + * Types of predicates we track. + */ +enum pred_type { + /** -readable */ + READABLE_PRED, + /** -writable */ + WRITABLE_PRED, + /** -executable */ + EXECUTABLE_PRED, + /** -acl */ + ACL_PRED, + /** -capable */ + CAPABLE_PRED, + /** -empty */ + EMPTY_PRED, + /** -hidden */ + HIDDEN_PRED, + /** -nogroup */ + NOGROUP_PRED, + /** -nouser */ + NOUSER_PRED, + /** -sparse */ + SPARSE_PRED, + /** -xattr */ + XATTR_PRED, + /** The number of pred_types. */ + PRED_TYPES, +}; + +/** Get the name of a predicate type. */ +static const char *pred_type_name(enum pred_type type) { + switch (type) { + case READABLE_PRED: + return "-readable"; + case WRITABLE_PRED: + return "-writable"; + case EXECUTABLE_PRED: + return "-executable"; + case ACL_PRED: + return "-acl"; + case CAPABLE_PRED: + return "-capable"; + case EMPTY_PRED: + return "-empty"; + case HIDDEN_PRED: + return "-hidden"; + case NOGROUP_PRED: + return "-nogroup"; + case NOUSER_PRED: + return "-nouser"; + case SPARSE_PRED: + return "-sparse"; + case XATTR_PRED: + return "-xattr"; + + case PRED_TYPES: + break; + } + + bfs_bug("Unknown predicate %d", (int)type); + return "???"; +} + /** * A contrained integer range. */ @@ -97,6 +163,11 @@ static void range_init_top(struct df_range *range) { range->max = LLONG_MAX; } +/** Check for an infinite range. */ +static bool range_is_top(const struct df_range *range) { + return range->min == 0 && range->max == LLONG_MAX; +} + /** Compute the minimum of two values. */ static long long min_value(long long a, long long b) { if (a < b) { @@ -170,35 +241,29 @@ enum range_type { RANGE_TYPES, }; -/** - * Types of predicates we track. - */ -enum pred_type { - /** -readable */ - READABLE_PRED, - /** -writable */ - WRITABLE_PRED, - /** -executable */ - EXECUTABLE_PRED, - /** -acl */ - ACL_PRED, - /** -capable */ - CAPABLE_PRED, - /** -empty */ - EMPTY_PRED, - /** -hidden */ - HIDDEN_PRED, - /** -nogroup */ - NOGROUP_PRED, - /** -nouser */ - NOUSER_PRED, - /** -sparse */ - SPARSE_PRED, - /** -xattr */ - XATTR_PRED, - /** The number of pred_types. */ - PRED_TYPES, -}; +/** Get the name of a range type. */ +static const char *range_type_name(enum range_type type) { + switch (type) { + case DEPTH_RANGE: + return "-depth"; + case GID_RANGE: + return "-gid"; + case INUM_RANGE: + return "-inum"; + case LINKS_RANGE: + return "-links"; + case SIZE_RANGE: + return "-size"; + case UID_RANGE: + return "-uid"; + + case RANGE_TYPES: + break; + } + + bfs_bug("Unknown range %d", (int)type); + return "???"; +} /** * The data flow analysis domain. @@ -210,9 +275,9 @@ struct df_domain { /** The value ranges we track. */ struct df_range ranges[RANGE_TYPES]; - /** Bitmask of possible file types. */ + /** Bitmask of possible -types. */ unsigned int types; - /** Bitmask of possible link target types. */ + /** Bitmask of possible -xtypes. */ unsigned int xtypes; }; @@ -265,6 +330,31 @@ static void df_init_top(struct df_domain *value) { value->xtypes = ~0; } +/** Check for the top element. */ +static bool df_is_top(const struct df_domain *value) { + for (int i = 0; i < PRED_TYPES; ++i) { + if (value->preds[i] != PRED_TOP) { + return false; + } + } + + for (int i = 0; i < RANGE_TYPES; ++i) { + if (!range_is_top(&value->ranges[i])) { + return false; + } + } + + if (value->types != ~0U) { + return false; + } + + if (value->xtypes != ~0U) { + return false; + } + + return true; +} + /** Compute the union of two fact sets. */ static void df_join(struct df_domain *dest, const struct df_domain *src) { for (int i = 0; i < PRED_TYPES; ++i) { @@ -285,6 +375,15 @@ static void df_join(struct df_domain *dest, const struct df_domain *src) { struct bfs_opt { /** The context we're optimizing. */ struct bfs_ctx *ctx; + /** Optimization level (ctx->optlevel). */ + int level; + /** Recursion depth. */ + int depth; + + /** Whether to produce warnings. */ + bool warn; + /** Whether the result of this expression is ignored. */ + bool ignore_result; /** Data flow state before this expression is evaluated. */ struct df_domain before; @@ -296,18 +395,14 @@ struct bfs_opt { struct df_domain *impure; }; -/** Constrain the value of a predicate. */ -static void opt_constrain_pred(struct bfs_opt *opt, enum pred_type type, bool value) { - constrain_pred(&opt->after_true.preds[type], value); - constrain_pred(&opt->after_false.preds[type], !value); -} - /** Log an optimization. */ -attr(printf(3, 4)) -static bool opt_debug(const struct bfs_opt *opt, int level, const char *format, ...) { - bfs_assert(opt->ctx->optlevel >= level); +attr(printf(2, 3)) +static bool opt_debug(struct bfs_opt *opt, const char *format, ...) { + if (bfs_debug_prefix(opt->ctx, DEBUG_OPT)) { + for (int i = 0; i < opt->depth; ++i) { + cfprintf(opt->ctx->cerr, "│ "); + } - if (bfs_debug(opt->ctx, DEBUG_OPT, "${cyn}-O%d${rs}: ", level)) { va_list args; va_start(args, format); cvfprintf(opt->ctx->cerr, format, args); @@ -318,450 +413,577 @@ static bool opt_debug(const struct bfs_opt *opt, int level, const char *format, } } -/** Warn about an expression. */ -attr(printf(3, 4)) -static void opt_warning(const struct bfs_opt *opt, const struct bfs_expr *expr, const char *format, ...) { - if (bfs_expr_warning(opt->ctx, expr)) { +/** Log a recursive call. */ +attr(printf(2, 3)) +static bool opt_enter(struct bfs_opt *opt, const char *format, ...) { + int depth = opt->depth; + if (depth > 0) { + --opt->depth; + } + + bool debug = opt_debug(opt, "%s", depth > 0 ? "├─╮ " : ""); + if (debug) { va_list args; va_start(args, format); - bfs_vwarning(opt->ctx, format, args); + cvfprintf(opt->ctx->cerr, format, args); va_end(args); } -} -/** Create a constant expression. */ -static struct bfs_expr *opt_const(const struct bfs_opt *opt, bool value) { - static bfs_eval_fn *fns[] = {eval_false, eval_true}; - static char *fake_args[] = {"-false", "-true"}; - return bfs_expr_new(opt->ctx, fns[value], 1, &fake_args[value]); + opt->depth = depth + 1; + return debug; } -/** - * Negate an expression. - */ -static struct bfs_expr *negate_expr(const struct bfs_opt *opt, struct bfs_expr *rhs, char **argv) { - if (rhs->eval_fn == eval_not) { - return rhs->rhs; - } +/** Log a recursive return. */ +attr(printf(2, 3)) +static bool opt_leave(struct bfs_opt *opt, const char *format, ...) { + bool debug = false; + int depth = opt->depth; - struct bfs_expr *expr = bfs_expr_new(opt->ctx, eval_not, 1, argv); - if (!expr) { - return NULL; + if (format) { + if (depth > 1) { + opt->depth -= 2; + } + + debug = opt_debug(opt, "%s", depth > 1 ? "├─╯ " : ""); + if (debug) { + va_list args; + va_start(args, format); + cvfprintf(opt->ctx->cerr, format, args); + va_end(args); + } } - expr->lhs = NULL; - expr->rhs = rhs; - return expr; + opt->depth = depth - 1; + return debug; } -static struct bfs_expr *optimize_not_expr(const struct bfs_opt *opt, struct bfs_expr *expr); -static struct bfs_expr *optimize_and_expr(const struct bfs_opt *opt, struct bfs_expr *expr); -static struct bfs_expr *optimize_or_expr(const struct bfs_opt *opt, struct bfs_expr *expr); - -/** - * Apply De Morgan's laws. - */ -static struct bfs_expr *de_morgan(const struct bfs_opt *opt, struct bfs_expr *expr, char **argv) { - bool debug = opt_debug(opt, 1, "De Morgan's laws: %pe ", expr); - - struct bfs_expr *parent = negate_expr(opt, expr, argv); - if (!parent) { - return NULL; +/** Log a shallow visit. */ +attr(printf(2, 3)) +static bool opt_visit(struct bfs_opt *opt, const char *format, ...) { + int depth = opt->depth; + if (depth > 0) { + --opt->depth; } - bool has_parent = true; - if (parent->eval_fn != eval_not) { - expr = parent; - has_parent = false; + bool debug = opt_debug(opt, "%s", depth > 0 ? "├─◯ " : ""); + if (debug) { + va_list args; + va_start(args, format); + cvfprintf(opt->ctx->cerr, format, args); + va_end(args); } - bfs_assert(expr->eval_fn == eval_and || expr->eval_fn == eval_or); - if (expr->eval_fn == eval_and) { - expr->eval_fn = eval_or; - expr->argv = &fake_or_arg; - } else { - expr->eval_fn = eval_and; - expr->argv = &fake_and_arg; - } + opt->depth = depth; + return debug; +} - expr->lhs = negate_expr(opt, expr->lhs, argv); - expr->rhs = negate_expr(opt, expr->rhs, argv); - if (!expr->lhs || !expr->rhs) { - return NULL; +/** Log the deletion of an expression. */ +attr(printf(2, 3)) +static bool opt_delete(struct bfs_opt *opt, const char *format, ...) { + int depth = opt->depth; + + if (depth > 0) { + --opt->depth; } + bool debug = opt_debug(opt, "%s", depth > 0 ? "├─✘ " : ""); if (debug) { - cfprintf(opt->ctx->cerr, "<==> %pe\n", parent); + va_list args; + va_start(args, format); + cvfprintf(opt->ctx->cerr, format, args); + va_end(args); } - if (expr->lhs->eval_fn == eval_not) { - expr->lhs = optimize_not_expr(opt, expr->lhs); - } - if (expr->rhs->eval_fn == eval_not) { - expr->rhs = optimize_not_expr(opt, expr->rhs); - } - if (!expr->lhs || !expr->rhs) { - return NULL; - } + opt->depth = depth; + return debug; +} - if (expr->eval_fn == eval_and) { - expr = optimize_and_expr(opt, expr); - } else { - expr = optimize_or_expr(opt, expr); +typedef bool dump_fn(struct bfs_opt *opt, const char *format, ...); + +/** Print a df_pred. */ +static void pred_dump(dump_fn *dump, struct bfs_opt *opt, const struct df_domain *value, enum pred_type type) { + dump(opt, "${blu}%s${rs}: ", pred_type_name(type)); + + FILE *file = opt->ctx->cerr->file; + switch (value->preds[type]) { + case PRED_BOTTOM: + fprintf(file, "⊥\n"); + break; + case PRED_TOP: + fprintf(file, "⊤\n"); + break; + case PRED_TRUE: + fprintf(file, "true\n"); + break; + case PRED_FALSE: + fprintf(file, "false\n"); + break; } - if (has_parent) { - parent->rhs = expr; +} + +/** Print a df_range. */ +static void range_dump(dump_fn *dump, struct bfs_opt *opt, const struct df_domain *value, enum range_type type) { + dump(opt, "${blu}%s${rs}: ", range_type_name(type)); + + FILE *file = opt->ctx->cerr->file; + const struct df_range *range = &value->ranges[type]; + if (range_is_bottom(range)) { + fprintf(file, "⊥\n"); + } else if (range_is_top(range)) { + fprintf(file, "⊤\n"); + } else if (range->min == range->max) { + fprintf(file, "%lld\n", range->min); } else { - parent = expr; - } - if (!expr) { - return NULL; + if (range->min == LLONG_MIN) { + fprintf(file, "(-∞, "); + } else { + fprintf(file, "[%lld, ", range->min); + } + if (range->max == LLONG_MAX) { + fprintf(file, "∞)\n"); + } else { + fprintf(file, "%lld]\n", range->max); + } } +} - if (has_parent) { - parent = optimize_not_expr(opt, parent); +/** Print a set of types. */ +static void types_dump(dump_fn *dump, struct bfs_opt *opt, const char *name, unsigned int types) { + dump(opt, "${blu}%s${rs}: ", name); + + FILE *file = opt->ctx->cerr->file; + if (types == 0) { + fprintf(file, " ⊥\n"); + } else if (types == ~0U) { + fprintf(file, " ⊤\n"); + } else if (count_ones(types) < count_ones(~types)) { + fprintf(file, " 0x%X\n", types); + } else { + fprintf(file, "~0x%X\n", ~types); } - return parent; } -/** Optimize an expression recursively. */ -static struct bfs_expr *optimize_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr); +/** Calculate the number of lines of df_dump() output. */ +static int df_dump_lines(const struct df_domain *value) { + int lines = 0; -/** - * Optimize a negation. - */ -static struct bfs_expr *optimize_not_expr(const struct bfs_opt *opt, struct bfs_expr *expr) { - bfs_assert(expr->eval_fn == eval_not); - - struct bfs_expr *rhs = expr->rhs; - - int optlevel = opt->ctx->optlevel; - if (optlevel >= 1) { - if (rhs->eval_fn == eval_true || rhs->eval_fn == eval_false) { - struct bfs_expr *ret = opt_const(opt, rhs->eval_fn == eval_false); - opt_debug(opt, 1, "constant propagation: %pe <==> %pe\n", expr, ret); - return ret; - } else if (rhs->eval_fn == eval_not) { - opt_debug(opt, 1, "double negation: %pe <==> %pe\n", expr, rhs->rhs); - return rhs->rhs; - } else if (bfs_expr_never_returns(rhs)) { - opt_debug(opt, 1, "reachability: %pe <==> %pe\n", expr, rhs); - return expr->rhs; - } else if ((rhs->eval_fn == eval_and || rhs->eval_fn == eval_or) - && (rhs->lhs->eval_fn == eval_not || rhs->rhs->eval_fn == eval_not)) { - return de_morgan(opt, expr, expr->argv); - } + for (int i = 0; i < PRED_TYPES; ++i) { + lines += value->preds[i] != PRED_TOP; } - expr->pure = rhs->pure; - expr->always_true = rhs->always_false; - expr->always_false = rhs->always_true; - expr->cost = rhs->cost; - expr->probability = 1.0 - rhs->probability; + for (int i = 0; i < RANGE_TYPES; ++i) { + lines += !range_is_top(&value->ranges[i]); + } - return expr; + lines += value->types != ~0U; + lines += value->xtypes != ~0U; + + return lines; } -/** Optimize a negation recursively. */ -static struct bfs_expr *optimize_not_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { - struct bfs_opt rhs_state = *opt; - expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); - if (!expr->rhs) { - return NULL; +/** Get the right debugging function for a df_dump() line. */ +static dump_fn *df_dump_line(int lines, int *line) { + ++*line; + + if (lines == 1) { + return opt_visit; + } else if (*line == 1) { + return opt_enter; + } else if (*line == lines) { + return opt_leave; + } else { + return opt_debug; } +} - opt->after_true = rhs_state.after_false; - opt->after_false = rhs_state.after_true; - - return optimize_not_expr(opt, expr); -} - -/** Optimize a conjunction. */ -static struct bfs_expr *optimize_and_expr(const struct bfs_opt *opt, struct bfs_expr *expr) { - bfs_assert(expr->eval_fn == eval_and); - - struct bfs_expr *lhs = expr->lhs; - struct bfs_expr *rhs = expr->rhs; - - const struct bfs_ctx *ctx = opt->ctx; - int optlevel = ctx->optlevel; - if (optlevel >= 1) { - if (lhs->eval_fn == eval_true) { - opt_debug(opt, 1, "conjunction elimination: %pe <==> %pe\n", expr, rhs); - return expr->rhs; - } else if (rhs->eval_fn == eval_true) { - opt_debug(opt, 1, "conjunction elimination: %pe <==> %pe\n", expr, lhs); - return expr->lhs; - } else if (lhs->always_false) { - opt_debug(opt, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); - opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); - return expr->lhs; - } else if (lhs->always_true && rhs->eval_fn == eval_false) { - bool debug = opt_debug(opt, 1, "strength reduction: %pe <==> ", expr); - struct bfs_expr *ret = expr->lhs; - ret = negate_expr(opt, ret, &fake_not_arg); - if (debug && ret) { - cfprintf(ctx->cerr, "%pe\n", ret); - } - return ret; - } else if (optlevel >= 2 && lhs->pure && rhs->eval_fn == eval_false) { - opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); - opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); - return expr->rhs; - } else if (lhs->eval_fn == eval_not && rhs->eval_fn == eval_not) { - return de_morgan(opt, expr, expr->lhs->argv); - } +/** Print a data flow value. */ +static void df_dump(struct bfs_opt *opt, const char *str, const struct df_domain *value) { + if (df_is_bottom(value)) { + opt_debug(opt, "%s: ⊥\n", str); + return; + } else if (df_is_top(value)) { + opt_debug(opt, "%s: ⊤\n", str); + return; } - expr->pure = lhs->pure && rhs->pure; - expr->always_true = lhs->always_true && rhs->always_true; - expr->always_false = lhs->always_false || rhs->always_false; - expr->cost = lhs->cost + lhs->probability * rhs->cost; - expr->probability = lhs->probability * rhs->probability; + if (!opt_debug(opt, "%s:\n", str)) { + return; + } - return expr; -} + int lines = df_dump_lines(value); + int line = 0; -/** Optimize a conjunction recursively. */ -static struct bfs_expr *optimize_and_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { - struct bfs_opt lhs_state = *opt; - expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); - if (!expr->lhs) { - return NULL; + for (int i = 0; i < PRED_TYPES; ++i) { + if (value->preds[i] != PRED_TOP) { + pred_dump(df_dump_line(lines, &line), opt, value, i); + } } - struct bfs_opt rhs_state = *opt; - rhs_state.before = lhs_state.after_true; - expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); - if (!expr->rhs) { - return NULL; + for (int i = 0; i < RANGE_TYPES; ++i) { + if (!range_is_top(&value->ranges[i])) { + range_dump(df_dump_line(lines, &line), opt, value, i); + } } - opt->after_true = rhs_state.after_true; - opt->after_false = lhs_state.after_false; - df_join(&opt->after_false, &rhs_state.after_false); - - return optimize_and_expr(opt, expr); -} - -/** Optimize a disjunction. */ -static struct bfs_expr *optimize_or_expr(const struct bfs_opt *opt, struct bfs_expr *expr) { - bfs_assert(expr->eval_fn == eval_or); - - struct bfs_expr *lhs = expr->lhs; - struct bfs_expr *rhs = expr->rhs; - - const struct bfs_ctx *ctx = opt->ctx; - int optlevel = ctx->optlevel; - if (optlevel >= 1) { - if (lhs->always_true) { - opt_debug(opt, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); - opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); - return expr->lhs; - } else if (lhs->eval_fn == eval_false) { - opt_debug(opt, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, rhs); - return expr->rhs; - } else if (rhs->eval_fn == eval_false) { - opt_debug(opt, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, lhs); - return expr->lhs; - } else if (lhs->always_false && rhs->eval_fn == eval_true) { - bool debug = opt_debug(opt, 1, "strength reduction: %pe <==> ", expr); - struct bfs_expr *ret = expr->lhs; - ret = negate_expr(opt, ret, &fake_not_arg); - if (debug && ret) { - cfprintf(ctx->cerr, "%pe\n", ret); - } - return ret; - } else if (optlevel >= 2 && lhs->pure && rhs->eval_fn == eval_true) { - opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); - opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); - return expr->rhs; - } else if (lhs->eval_fn == eval_not && rhs->eval_fn == eval_not) { - return de_morgan(opt, expr, expr->lhs->argv); - } + if (value->types != ~0U) { + types_dump(df_dump_line(lines, &line), opt, "-type", value->types); } - expr->pure = lhs->pure && rhs->pure; - expr->always_true = lhs->always_true || rhs->always_true; - expr->always_false = lhs->always_false && rhs->always_false; - expr->cost = lhs->cost + (1 - lhs->probability) * rhs->cost; - expr->probability = lhs->probability + rhs->probability - lhs->probability * rhs->probability; + if (value->xtypes != ~0U) { + types_dump(df_dump_line(lines, &line), opt, "-xtype", value->xtypes); + } +} - return expr; +/** Check if an expression is constant. */ +static bool is_const(const struct bfs_expr *expr) { + return expr->eval_fn == eval_true || expr->eval_fn == eval_false; } -/** Optimize a disjunction recursively. */ -static struct bfs_expr *optimize_or_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { - struct bfs_opt lhs_state = *opt; - expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); - if (!expr->lhs) { - return NULL; +/** Warn about an expression. */ +attr(printf(3, 4)) +static void opt_warning(const struct bfs_opt *opt, const struct bfs_expr *expr, const char *format, ...) { + if (!opt->warn) { + return; } - struct bfs_opt rhs_state = *opt; - rhs_state.before = lhs_state.after_false; - expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); - if (!expr->rhs) { - return NULL; + if (bfs_expr_is_parent(expr) || is_const(expr)) { + return; } - opt->after_false = rhs_state.after_false; - opt->after_true = lhs_state.after_true; - df_join(&opt->after_true, &rhs_state.after_true); - - return optimize_or_expr(opt, expr); + if (bfs_expr_warning(opt->ctx, expr)) { + va_list args; + va_start(args, format); + bfs_vwarning(opt->ctx, format, args); + va_end(args); + } } -/** Optimize an expression in an ignored-result context. */ -static struct bfs_expr *ignore_result(const struct bfs_opt *opt, struct bfs_expr *expr) { - int optlevel = opt->ctx->optlevel; +/** Remove and return an expression's children. */ +static void foster_children(struct bfs_expr *expr, struct bfs_exprs *children) { + bfs_assert(bfs_expr_is_parent(expr)); - if (optlevel >= 1) { - while (true) { - if (expr->eval_fn == eval_not) { - opt_debug(opt, 1, "ignored result: %pe --> %pe\n", expr, expr->rhs); - opt_warning(opt, expr, "The result of this expression is ignored.\n\n"); - expr = expr->rhs; - } else if (optlevel >= 2 - && (expr->eval_fn == eval_and || expr->eval_fn == eval_or || expr->eval_fn == eval_comma) - && expr->rhs->pure) { - opt_debug(opt, 2, "ignored result: %pe --> %pe\n", expr, expr->lhs); - opt_warning(opt, expr->rhs, "The result of this expression is ignored.\n\n"); - expr = expr->lhs; - } else { - break; - } - } + SLIST_INIT(children); + SLIST_EXTEND(children, &expr->children); - if (optlevel >= 2 && expr->pure && expr->eval_fn != eval_false) { - struct bfs_expr *ret = opt_const(opt, false); - opt_debug(opt, 2, "ignored result: %pe --> %pe\n", expr, ret); - opt_warning(opt, expr, "The result of this expression is ignored.\n\n"); - return ret; - } - } + expr->persistent_fds = 0; + expr->ephemeral_fds = 0; + expr->pure = true; +} - return expr; +/** Return an expression's only child. */ +static struct bfs_expr *only_child(struct bfs_expr *expr) { + bfs_assert(bfs_expr_is_parent(expr)); + struct bfs_expr *child = bfs_expr_children(expr); + bfs_assert(child && !child->next); + return child; } -/** Optimize a comma expression. */ -static struct bfs_expr *optimize_comma_expr(const struct bfs_opt *opt, struct bfs_expr *expr) { - bfs_assert(expr->eval_fn == eval_comma); +/** Foster an expression's only child. */ +static struct bfs_expr *foster_only_child(struct bfs_expr *expr) { + struct bfs_expr *child = only_child(expr); + struct bfs_exprs children; + foster_children(expr, &children); + return child; +} - struct bfs_expr *lhs = expr->lhs; - struct bfs_expr *rhs = expr->rhs; - - int optlevel = opt->ctx->optlevel; - if (optlevel >= 1) { - lhs = expr->lhs = ignore_result(opt, lhs); - - if (bfs_expr_never_returns(lhs)) { - opt_debug(opt, 1, "reachability: %pe <==> %pe\n", expr, lhs); - opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); - return expr->lhs; - } else if ((lhs->always_true && rhs->eval_fn == eval_true) - || (lhs->always_false && rhs->eval_fn == eval_false)) { - opt_debug(opt, 1, "redundancy elimination: %pe <==> %pe\n", expr, lhs); - return expr->lhs; - } else if (optlevel >= 2 && lhs->pure) { - opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); - opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); - return expr->rhs; +/** An expression visitor. */ +struct visitor; + +/** An expression-visiting function. */ +typedef struct bfs_expr *visit_fn(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor); + +/** An entry in a visitor lookup table. */ +struct visitor_table { + /** The evaluation function to match on. */ + bfs_eval_fn *eval_fn; + /** The visitor function. */ + visit_fn *visit; +}; + +/** Look up a visitor in a table. */ +static visit_fn *look_up_visitor(const struct bfs_expr *expr, const struct visitor_table table[]) { + for (size_t i = 0; table[i].eval_fn; ++i) { + if (expr->eval_fn == table[i].eval_fn) { + return table[i].visit; } } - expr->pure = lhs->pure && rhs->pure; - expr->always_true = bfs_expr_never_returns(lhs) || rhs->always_true; - expr->always_false = bfs_expr_never_returns(lhs) || rhs->always_false; - expr->cost = lhs->cost + rhs->cost; - expr->probability = rhs->probability; - - return expr; + return NULL; } -/** Optimize a comma expression recursively. */ -static struct bfs_expr *optimize_comma_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { - struct bfs_opt lhs_state = *opt; - expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); - if (!expr->lhs) { - return NULL; - } +struct visitor { + /** The name of this visitor. */ + const char *name; + + /** A function to call before visiting children. */ + visit_fn *enter; + /** The default visitor. */ + visit_fn *visit; + /** A function to call after visiting children. */ + visit_fn *leave; + + /** A visitor lookup table. */ + struct visitor_table table[]; +}; - struct bfs_opt rhs_state = *opt; - rhs_state.before = lhs_state.after_true; - df_join(&rhs_state.before, &lhs_state.after_false); +/** Recursive visitor implementation. */ +static struct bfs_expr *visit_deep(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor); - expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); - if (!expr->rhs) { +/** Visit a negation. */ +static struct bfs_expr *visit_not(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_expr *rhs = foster_only_child(expr); + + struct bfs_opt nested = *opt; + rhs = visit_deep(&nested, rhs, visitor); + if (!rhs) { return NULL; } - return optimize_comma_expr(opt, expr); + opt->after_true = nested.after_false; + opt->after_false = nested.after_true; + + bfs_expr_append(expr, rhs); + return expr; } -/** Optimize an icmp-style ([+-]N) expression. */ -static void optimize_icmp(struct bfs_opt *opt, const struct bfs_expr *expr, enum range_type type) { - struct df_range *true_range = &opt->after_true.ranges[type]; - struct df_range *false_range = &opt->after_false.ranges[type]; - long long value = expr->num; +/** Visit a conjunction. */ +static struct bfs_expr *visit_and(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_exprs children; + foster_children(expr, &children); - switch (expr->int_cmp) { - case BFS_INT_EQUAL: - constrain_min(true_range, value); - constrain_max(true_range, value); - range_remove(false_range, value); - break; + // Base case (-and) == (-true) + df_init_bottom(&opt->after_false); + struct bfs_opt nested = *opt; - case BFS_INT_LESS: - constrain_min(false_range, value); - constrain_max(true_range, value); - range_remove(true_range, value); - break; + while (!SLIST_EMPTY(&children)) { + struct bfs_expr *child = SLIST_POP(&children); - case BFS_INT_GREATER: - constrain_max(false_range, value); - constrain_min(true_range, value); - range_remove(true_range, value); - break; - } -} + if (SLIST_EMPTY(&children)) { + nested.ignore_result = opt->ignore_result; + } else { + nested.ignore_result = false; + } -/** Optimize -{execut,read,writ}able. */ -static struct bfs_expr *optimize_access(struct bfs_opt *opt, struct bfs_expr *expr) { - expr->probability = 1.0; + child = visit_deep(&nested, child, visitor); + if (!child) { + return NULL; + } - if (expr->num & R_OK) { - opt_constrain_pred(opt, READABLE_PRED, true); - expr->probability *= 0.99; - } + df_join(&opt->after_false, &nested.after_false); + nested.before = nested.after_true; - if (expr->num & W_OK) { - opt_constrain_pred(opt, WRITABLE_PRED, true); - expr->probability *= 0.8; + bfs_expr_append(expr, child); } - if (expr->num & X_OK) { - opt_constrain_pred(opt, EXECUTABLE_PRED, true); - expr->probability *= 0.2; - } + opt->after_true = nested.after_true; return expr; } -/** Optimize -empty. */ -static struct bfs_expr *optimize_empty(struct bfs_opt *opt, struct bfs_expr *expr) { - if (opt->ctx->optlevel >= 4) { - // Since -empty attempts to open and read directories, it may - // have side effects such as reporting permission errors, and - // thus shouldn't be re-ordered without aggressive optimizations - expr->pure = true; - } +/** Visit a disjunction. */ +static struct bfs_expr *visit_or(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_exprs children; + foster_children(expr, &children); - return expr; -} + // Base case (-or) == (-false) + df_init_bottom(&opt->after_true); + struct bfs_opt nested = *opt; -/** Optimize -{exec,ok}{,dir}. */ -static struct bfs_expr *optimize_exec(struct bfs_opt *opt, struct bfs_expr *expr) { + while (!SLIST_EMPTY(&children)) { + struct bfs_expr *child = SLIST_POP(&children); + + if (SLIST_EMPTY(&children)) { + nested.ignore_result = opt->ignore_result; + } else { + nested.ignore_result = false; + } + + child = visit_deep(&nested, child, visitor); + if (!child) { + return NULL; + } + + df_join(&opt->after_true, &nested.after_true); + nested.before = nested.after_false; + + bfs_expr_append(expr, child); + } + + opt->after_false = nested.after_false; + + return expr; +} + +/** Visit a comma expression. */ +static struct bfs_expr *visit_comma(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_exprs children; + foster_children(expr, &children); + + struct bfs_opt nested = *opt; + + while (!SLIST_EMPTY(&children)) { + struct bfs_expr *child = SLIST_POP(&children); + + if (SLIST_EMPTY(&children)) { + nested.ignore_result = opt->ignore_result; + } else { + nested.ignore_result = true; + } + + child = visit_deep(&nested, child, visitor); + if (!child) { + return NULL; + } + + nested.before = nested.after_true; + df_join(&nested.before, &nested.after_false); + + bfs_expr_append(expr, child); + } + + opt->after_true = nested.after_true; + opt->after_false = nested.after_false; + + return expr; +} + +/** Default enter() function. */ +static struct bfs_expr *visit_enter(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + opt_enter(opt, "%pe\n", expr); + opt->after_true = opt->before; + opt->after_false = opt->before; + return expr; +} + +/** Default leave() function. */ +static struct bfs_expr *visit_leave(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + opt_leave(opt, "%pe\n", expr); + return expr; +} + +static struct bfs_expr *visit_deep(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + bool entered = false; + + visit_fn *enter = visitor->enter ? visitor->enter : visit_enter; + visit_fn *leave = visitor->leave ? visitor->leave : visit_leave; + + static const struct visitor_table table[] = { + {eval_not, visit_not}, + {eval_and, visit_and}, + {eval_or, visit_or}, + {eval_comma, visit_comma}, + {NULL, NULL}, + }; + visit_fn *recursive = look_up_visitor(expr, table); + if (recursive) { + if (!entered) { + expr = enter(opt, expr, visitor); + if (!expr) { + return NULL; + } + entered = true; + } + + expr = recursive(opt, expr, visitor); + if (!expr) { + return NULL; + } + } + + visit_fn *general = visitor->visit; + if (general) { + if (!entered) { + expr = enter(opt, expr, visitor); + if (!expr) { + return NULL; + } + entered = true; + } + + expr = general(opt, expr, visitor); + if (!expr) { + return NULL; + } + } + + visit_fn *specific = look_up_visitor(expr, visitor->table); + if (specific) { + if (!entered) { + expr = enter(opt, expr, visitor); + if (!expr) { + return NULL; + } + entered = true; + } + + expr = specific(opt, expr, visitor); + if (!expr) { + return NULL; + } + } + + if (entered) { + expr = leave(opt, expr, visitor); + } else { + opt_visit(opt, "%pe\n", expr); + } + + return expr; +} + +/** Visit an expression recursively. */ +static struct bfs_expr *visit(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + opt_enter(opt, "%s()\n", visitor->name); + expr = visit_deep(opt, expr, visitor); + opt_leave(opt, "\n"); + return expr; +} + +/** Visit an expression non-recursively. */ +static struct bfs_expr *visit_shallow(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + visit_fn *general = visitor->visit; + if (expr && general) { + expr = general(opt, expr, visitor); + } + + visit_fn *specific = look_up_visitor(expr, visitor->table); + if (expr && specific) { + expr = specific(opt, expr, visitor); + } + + return expr; +} + +/** Annotate -{execut,read,writ}able. */ +static struct bfs_expr *annotate_access(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + expr->probability = 1.0; + if (expr->num & R_OK) { + expr->probability *= 0.99; + } + if (expr->num & W_OK) { + expr->probability *= 0.8; + } + if (expr->num & X_OK) { + expr->probability *= 0.2; + } + + return expr; +} + +/** Annotate -empty. */ +static struct bfs_expr *annotate_empty(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (opt->level >= 4) { + // Since -empty attempts to open and read directories, it may + // have side effects such as reporting permission errors, and + // thus shouldn't be re-ordered without aggressive optimizations + expr->pure = true; + } + + return expr; +} + +/** Annotate -exec. */ +static struct bfs_expr *annotate_exec(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { if (expr->exec->flags & BFS_EXEC_MULTI) { expr->always_true = true; } else { @@ -771,33 +993,650 @@ static struct bfs_expr *optimize_exec(struct bfs_opt *opt, struct bfs_expr *expr return expr; } -/** Optimize -name/-lname/-path. */ -static struct bfs_expr *optimize_fnmatch(struct bfs_opt *opt, struct bfs_expr *expr) { - if (strchr(expr->argv[1], '*')) { +/** Annotate -name/-lname/-path. */ +static struct bfs_expr *annotate_fnmatch(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (expr->literal) { + expr->probability = 0.1; + } else { expr->probability = 0.5; + } + + return expr; +} + +/** Estimate probability for -x?type. */ +static void estimate_type_probability(struct bfs_expr *expr) { + unsigned int types = expr->num; + + expr->probability = 0.0; + if (types & (1 << BFS_BLK)) { + expr->probability += 0.00000721183; + } + if (types & (1 << BFS_CHR)) { + expr->probability += 0.0000499855; + } + if (types & (1 << BFS_DIR)) { + expr->probability += 0.114475; + } + if (types & (1 << BFS_DOOR)) { + expr->probability += 0.000001; + } + if (types & (1 << BFS_FIFO)) { + expr->probability += 0.00000248684; + } + if (types & (1 << BFS_REG)) { + expr->probability += 0.859772; + } + if (types & (1 << BFS_LNK)) { + expr->probability += 0.0256816; + } + if (types & (1 << BFS_SOCK)) { + expr->probability += 0.0000116881; + } + if (types & (1 << BFS_WHT)) { + expr->probability += 0.000001; + } +} + +/** Annotate -type. */ +static struct bfs_expr *annotate_type(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + estimate_type_probability(expr); + return expr; +} + +/** Annotate -xtype. */ +static struct bfs_expr *annotate_xtype(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (opt->level >= 4) { + // Since -xtype dereferences symbolic links, it may have side + // effects such as reporting permission errors, and thus + // shouldn't be re-ordered without aggressive optimizations + expr->pure = true; + } + + estimate_type_probability(expr); + return expr; +} + +/** Annotate a negation. */ +static struct bfs_expr *annotate_not(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_expr *rhs = only_child(expr); + expr->pure = rhs->pure; + expr->always_true = rhs->always_false; + expr->always_false = rhs->always_true; + expr->cost = rhs->cost; + expr->probability = 1.0 - rhs->probability; + return expr; +} + +/** Annotate a conjunction. */ +static struct bfs_expr *annotate_and(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + expr->pure = true; + expr->always_true = true; + expr->always_false = false; + expr->cost = 0.0; + expr->probability = 1.0; + + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + expr->pure &= child->pure; + expr->always_true &= child->always_true; + expr->always_false |= child->always_false; + expr->cost += expr->probability * child->cost; + expr->probability *= child->probability; + } + + return expr; +} + +/** Annotate a disjunction. */ +static struct bfs_expr *annotate_or(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + expr->pure = true; + expr->always_true = false; + expr->always_false = true; + expr->cost = 0.0; + + float false_prob = 1.0; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + expr->pure &= child->pure; + expr->always_true |= child->always_true; + expr->always_false &= child->always_false; + expr->cost += false_prob * child->cost; + false_prob *= (1.0 - child->probability); + } + expr->probability = 1.0 - false_prob; + + return expr; +} + +/** Annotate a comma expression. */ +static struct bfs_expr *annotate_comma(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + expr->pure = true; + expr->cost = 0.0; + + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + expr->pure &= child->pure; + expr->always_true = child->always_true; + expr->always_false = child->always_false; + expr->cost += child->cost; + expr->probability = child->probability; + } + + return expr; +} + +/** Annotate an arbitrary expression. */ +static struct bfs_expr *annotate_visit(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + /** Table of pure expressions. */ + static bfs_eval_fn *const pure[] = { + eval_access, + eval_acl, + eval_capable, + eval_depth, + eval_false, + eval_flags, + eval_fstype, + eval_gid, + eval_hidden, + eval_inum, + eval_links, + eval_lname, + eval_name, + eval_newer, + eval_nogroup, + eval_nouser, + eval_path, + eval_perm, + eval_regex, + eval_samefile, + eval_size, + eval_sparse, + eval_time, + eval_true, + eval_type, + eval_uid, + eval_used, + eval_xattr, + eval_xattrname, + }; + + expr->pure = false; + for (size_t i = 0; i < countof(pure); ++i) { + if (expr->eval_fn == pure[i]) { + expr->pure = true; + break; + } + } + + /** Table of always-true expressions. */ + static bfs_eval_fn *const always_true[] = { + eval_fls, + eval_fprint, + eval_fprint0, + eval_fprintf, + eval_fprintx, + eval_prune, + eval_true, + // Non-returning + eval_exit, + eval_quit, + }; + + expr->always_true = false; + for (size_t i = 0; i < countof(always_true); ++i) { + if (expr->eval_fn == always_true[i]) { + expr->always_true = true; + break; + } + } + + /** Table of always-false expressions. */ + static bfs_eval_fn *const always_false[] = { + eval_false, + // Non-returning + eval_exit, + eval_quit, + }; + + expr->always_false = false; + for (size_t i = 0; i < countof(always_false); ++i) { + if (expr->eval_fn == always_false[i]) { + expr->always_false = true; + break; + } + } + +#define FAST_COST 40.0 +#define FNMATCH_COST 400.0 +#define STAT_COST 1000.0 +#define PRINT_COST 20000.0 + + /** Table of expression costs. */ + static const struct { + bfs_eval_fn *eval_fn; + float cost; + } costs[] = { + {eval_access, STAT_COST}, + {eval_acl, STAT_COST}, + {eval_capable, STAT_COST}, + {eval_empty, 2 * STAT_COST}, // readdir() is worse than stat() + {eval_fls, PRINT_COST}, + {eval_fprint, PRINT_COST}, + {eval_fprint0, PRINT_COST}, + {eval_fprintf, PRINT_COST}, + {eval_fprintx, PRINT_COST}, + {eval_fstype, STAT_COST}, + {eval_gid, STAT_COST}, + {eval_inum, STAT_COST}, + {eval_links, STAT_COST}, + {eval_lname, FNMATCH_COST}, + {eval_name, FNMATCH_COST}, + {eval_newer, STAT_COST}, + {eval_nogroup, STAT_COST}, + {eval_nouser, STAT_COST}, + {eval_path, FNMATCH_COST}, + {eval_perm, STAT_COST}, + {eval_samefile, STAT_COST}, + {eval_size, STAT_COST}, + {eval_sparse, STAT_COST}, + {eval_time, STAT_COST}, + {eval_uid, STAT_COST}, + {eval_used, STAT_COST}, + {eval_xattr, STAT_COST}, + {eval_xattrname, STAT_COST}, + }; + + expr->cost = FAST_COST; + for (size_t i = 0; i < countof(costs); ++i) { + if (expr->eval_fn == costs[i].eval_fn) { + expr->cost = costs[i].cost; + break; + } + } + + /** Table of expression probabilities. */ + static const struct { + /** The evaluation function with this cost. */ + bfs_eval_fn *eval_fn; + /** The matching probability. */ + float probability; + } probs[] = { + {eval_acl, 0.00002}, + {eval_capable, 0.000002}, + {eval_empty, 0.01}, + {eval_false, 0.0}, + {eval_hidden, 0.01}, + {eval_nogroup, 0.01}, + {eval_nouser, 0.01}, + {eval_samefile, 0.01}, + {eval_true, 1.0}, + {eval_xattr, 0.01}, + {eval_xattrname, 0.01}, + }; + + expr->probability = 0.5; + for (size_t i = 0; i < countof(probs); ++i) { + if (expr->eval_fn == probs[i].eval_fn) { + expr->probability = probs[i].probability; + break; + } + } + + return expr; +} + +/** + * Annotating visitor. + */ +static const struct visitor annotate = { + .name = "annotate", + .visit = annotate_visit, + .table = { + {eval_access, annotate_access}, + {eval_empty, annotate_empty}, + {eval_exec, annotate_exec}, + {eval_lname, annotate_fnmatch}, + {eval_name, annotate_fnmatch}, + {eval_path, annotate_fnmatch}, + {eval_type, annotate_type}, + {eval_xtype, annotate_xtype}, + + {eval_not, annotate_not}, + {eval_and, annotate_and}, + {eval_or, annotate_or}, + {eval_comma, annotate_comma}, + + {NULL, NULL}, + }, +}; + +/** Create a constant expression. */ +static struct bfs_expr *opt_const(struct bfs_opt *opt, bool value) { + static bfs_eval_fn *const fns[] = {eval_false, eval_true}; + static char *fake_args[] = {"-false", "-true"}; + + struct bfs_expr *expr = bfs_expr_new(opt->ctx, fns[value], 1, &fake_args[value]); + return visit_shallow(opt, expr, &annotate); +} + +/** Negate an expression, keeping it canonical. */ +static struct bfs_expr *negate_expr(struct bfs_opt *opt, struct bfs_expr *expr, char **argv) { + if (expr->eval_fn == eval_not) { + return only_child(expr); + } else if (expr->eval_fn == eval_true) { + return opt_const(opt, false); + } else if (expr->eval_fn == eval_false) { + return opt_const(opt, true); + } + + struct bfs_expr *ret = bfs_expr_new(opt->ctx, eval_not, 1, argv); + if (!ret) { + return NULL; + } + + bfs_expr_append(ret, expr); + return visit_shallow(opt, ret, &annotate); +} + +/** Sink negations into a conjunction/disjunction using De Morgan's laws. */ +static struct bfs_expr *sink_not_andor(struct bfs_opt *opt, struct bfs_expr *expr) { + opt_debug(opt, "De Morgan's laws\n"); + + char **argv = expr->argv; + expr = only_child(expr); + opt_enter(opt, "%pe\n", expr); + + if (expr->eval_fn == eval_and) { + expr->eval_fn = eval_or; + expr->argv = &fake_or_arg; } else { - expr->probability = 0.1; + bfs_assert(expr->eval_fn == eval_or); + expr->eval_fn = eval_and; + expr->argv = &fake_and_arg; + } + + struct bfs_exprs children; + foster_children(expr, &children); + + struct bfs_expr *child; + while ((child = SLIST_POP(&children))) { + opt_enter(opt, "%pe\n", child); + + child = negate_expr(opt, child, argv); + if (!child) { + return NULL; + } + + opt_leave(opt, "%pe\n", child); + bfs_expr_append(expr, child); + } + + opt_leave(opt, "%pe\n", expr); + return visit_shallow(opt, expr, &annotate); +} + +/** Sink a negation into a comma expression. */ +static struct bfs_expr *sink_not_comma(struct bfs_opt *opt, struct bfs_expr *expr) { + bfs_assert(expr->eval_fn == eval_comma); + + opt_enter(opt, "%pe\n", expr); + + char **argv = expr->argv; + expr = only_child(expr); + + struct bfs_exprs children; + foster_children(expr, &children); + + struct bfs_expr *child; + while ((child = SLIST_POP(&children))) { + if (SLIST_EMPTY(&children)) { + opt_enter(opt, "%pe\n", child); + opt_debug(opt, "sink\n"); + + child = negate_expr(opt, child, argv); + if (!child) { + return NULL; + } + + opt_leave(opt, "%pe\n", child); + } else { + opt_visit(opt, "%pe\n", child); + } + + bfs_expr_append(expr, child); + } + + opt_leave(opt, "%pe\n", expr); + return visit_shallow(opt, expr, &annotate); +} + +/** Canonicalize a negation. */ +static struct bfs_expr *canonicalize_not(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_expr *rhs = only_child(expr); + + if (rhs->eval_fn == eval_not) { + opt_debug(opt, "double negation\n"); + rhs = only_child(expr); + return only_child(rhs); + } else if (rhs->eval_fn == eval_and || rhs->eval_fn == eval_or) { + return sink_not_andor(opt, expr); + } else if (rhs->eval_fn == eval_comma) { + return sink_not_comma(opt, expr); + } else if (is_const(rhs)) { + opt_debug(opt, "constant propagation\n"); + return opt_const(opt, rhs->eval_fn == eval_false); + } else { + return expr; + } +} + +/** Canonicalize an associative operator. */ +static struct bfs_expr *canonicalize_assoc(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_exprs children; + foster_children(expr, &children); + + struct bfs_exprs flat; + SLIST_INIT(&flat); + + struct bfs_expr *child; + while ((child = SLIST_POP(&children))) { + if (child->eval_fn == expr->eval_fn) { + struct bfs_expr *head = SLIST_HEAD(&child->children); + struct bfs_expr *tail = SLIST_TAIL(&child->children); + + if (!head) { + opt_delete(opt, "%pe [empty]\n", child); + } else { + opt_enter(opt, "%pe\n", child); + opt_debug(opt, "associativity\n"); + if (head == tail) { + opt_leave(opt, "%pe\n", head); + } else if (head->next == tail) { + opt_leave(opt, "%pe %pe\n", head, tail); + } else { + opt_leave(opt, "%pe ... %pe\n", head, tail); + } + } + + SLIST_EXTEND(&flat, &child->children); + } else { + opt_visit(opt, "%pe\n", child); + SLIST_APPEND(&flat, child); + } + } + + bfs_expr_extend(expr, &flat); + + return visit_shallow(opt, expr, &annotate); +} + +/** + * Canonicalizing visitor. + */ +static const struct visitor canonicalize = { + .name = "canonicalize", + .table = { + {eval_not, canonicalize_not}, + {eval_and, canonicalize_assoc}, + {eval_or, canonicalize_assoc}, + {eval_comma, canonicalize_assoc}, + {NULL, NULL}, + }, +}; + +/** Calculate the cost of an ordered pair of expressions. */ +static float expr_cost(const struct bfs_expr *parent, const struct bfs_expr *lhs, const struct bfs_expr *rhs) { + // https://cs.stackexchange.com/a/66921/21004 + float prob = lhs->probability; + if (parent->eval_fn == eval_or) { + prob = 1.0 - prob; + } + return lhs->cost + prob * rhs->cost; +} + +/** Sort a block of expressions. */ +static void sort_exprs(struct bfs_opt *opt, struct bfs_expr *parent, struct bfs_exprs *exprs) { + if (!exprs->head || !exprs->head->next) { + return; + } + + struct bfs_exprs left, right; + SLIST_INIT(&left); + SLIST_INIT(&right); + + // Split + for (struct bfs_expr *hare = exprs->head; hare && (hare = hare->next); hare = hare->next) { + struct bfs_expr *tortoise = SLIST_POP(exprs); + SLIST_APPEND(&left, tortoise); + } + SLIST_EXTEND(&right, exprs); + + // Recurse + sort_exprs(opt, parent, &left); + sort_exprs(opt, parent, &right); + + // Merge + while (!SLIST_EMPTY(&left) && !SLIST_EMPTY(&right)) { + struct bfs_expr *lhs = left.head; + struct bfs_expr *rhs = right.head; + + float cost = expr_cost(parent, lhs, rhs); + float swapped = expr_cost(parent, rhs, lhs); + + if (cost <= swapped) { + SLIST_POP(&left); + SLIST_APPEND(exprs, lhs); + } else { + opt_enter(opt, "%pe %pe [${ylw}%g${rs}]\n", lhs, rhs, cost); + SLIST_POP(&right); + SLIST_APPEND(exprs, rhs); + opt_leave(opt, "%pe %pe [${ylw}%g${rs}]\n", rhs, lhs, swapped); + } + } + SLIST_EXTEND(exprs, &left); + SLIST_EXTEND(exprs, &right); +} + +/** Reorder children to reduce cost. */ +static struct bfs_expr *reorder_andor(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_exprs children; + foster_children(expr, &children); + + // Split into blocks of consecutive pure/impure expressions, and sort + // the pure blocks + struct bfs_exprs pure; + SLIST_INIT(&pure); + + struct bfs_expr *child; + while ((child = SLIST_POP(&children))) { + if (child->pure) { + SLIST_APPEND(&pure, child); + } else { + sort_exprs(opt, expr, &pure); + bfs_expr_extend(expr, &pure); + bfs_expr_append(expr, child); + } + } + sort_exprs(opt, expr, &pure); + bfs_expr_extend(expr, &pure); + + return visit_shallow(opt, expr, &annotate); +} + +/** + * Reordering visitor. + */ +static const struct visitor reorder = { + .name = "reorder", + .table = { + {eval_and, reorder_andor}, + {eval_or, reorder_andor}, + {NULL, NULL}, + }, +}; + +/** Transfer function for simple predicates. */ +static void data_flow_pred(struct bfs_opt *opt, enum pred_type pred, bool value) { + constrain_pred(&opt->after_true.preds[pred], value); + constrain_pred(&opt->after_false.preds[pred], !value); +} + +/** Transfer function for icmp-style ([+-]N) expressions. */ +static void data_flow_icmp(struct bfs_opt *opt, const struct bfs_expr *expr, enum range_type type) { + struct df_range *true_range = &opt->after_true.ranges[type]; + struct df_range *false_range = &opt->after_false.ranges[type]; + long long value = expr->num; + + switch (expr->int_cmp) { + case BFS_INT_EQUAL: + constrain_min(true_range, value); + constrain_max(true_range, value); + range_remove(false_range, value); + break; + + case BFS_INT_LESS: + constrain_min(false_range, value); + constrain_max(true_range, value); + range_remove(true_range, value); + break; + + case BFS_INT_GREATER: + constrain_max(false_range, value); + constrain_min(true_range, value); + range_remove(true_range, value); + break; + } +} + +/** Transfer function for -{execut,read,writ}able. */ +static struct bfs_expr *data_flow_access(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (expr->num & R_OK) { + data_flow_pred(opt, READABLE_PRED, true); + } + if (expr->num & W_OK) { + data_flow_pred(opt, WRITABLE_PRED, true); + } + if (expr->num & X_OK) { + data_flow_pred(opt, EXECUTABLE_PRED, true); } return expr; } -/** Optimize -gid. */ -static struct bfs_expr *optimize_gid(struct bfs_opt *opt, struct bfs_expr *expr) { +/** Transfer function for -gid. */ +static struct bfs_expr *data_flow_gid(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { struct df_range *range = &opt->after_true.ranges[GID_RANGE]; if (range->min == range->max) { gid_t gid = range->min; bool nogroup = !bfs_getgrgid(opt->ctx->groups, gid); if (errno == 0) { - opt_constrain_pred(opt, NOGROUP_PRED, nogroup); + data_flow_pred(opt, NOGROUP_PRED, nogroup); } } return expr; } -/** Optimize -inum. */ -static struct bfs_expr *optimize_inum(struct bfs_opt *opt, struct bfs_expr *expr) { +/** Transfer function for -inum. */ +static struct bfs_expr *data_flow_inum(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { struct df_range *range = &opt->after_true.ranges[INUM_RANGE]; if (range->min == range->max) { expr->probability = 0.01; @@ -808,8 +1647,8 @@ static struct bfs_expr *optimize_inum(struct bfs_opt *opt, struct bfs_expr *expr return expr; } -/** Optimize -links. */ -static struct bfs_expr *optimize_links(struct bfs_opt *opt, struct bfs_expr *expr) { +/** Transfer function for -links. */ +static struct bfs_expr *data_flow_links(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { struct df_range *range = &opt->after_true.ranges[LINKS_RANGE]; if (1 >= range->min && 1 <= range->max) { expr->probability = 0.99; @@ -820,30 +1659,20 @@ static struct bfs_expr *optimize_links(struct bfs_opt *opt, struct bfs_expr *exp return expr; } -/** Optimize -uid. */ -static struct bfs_expr *optimize_uid(struct bfs_opt *opt, struct bfs_expr *expr) { - struct df_range *range = &opt->after_true.ranges[UID_RANGE]; - if (range->min == range->max) { - uid_t uid = range->min; - bool nouser = !bfs_getpwuid(opt->ctx->users, uid); - if (errno == 0) { - opt_constrain_pred(opt, NOUSER_PRED, nouser); - } - } +/** Transfer function for -samefile. */ +static struct bfs_expr *data_flow_samefile(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct df_range *true_range = &opt->after_true.ranges[INUM_RANGE]; + constrain_min(true_range, expr->ino); + constrain_max(true_range, expr->ino); - return expr; -} + struct df_range *false_range = &opt->after_false.ranges[INUM_RANGE]; + range_remove(false_range, expr->ino); -/** Optimize -samefile. */ -static struct bfs_expr *optimize_samefile(struct bfs_opt *opt, struct bfs_expr *expr) { - struct df_range *range = &opt->after_true.ranges[INUM_RANGE]; - constrain_min(range, expr->ino); - constrain_max(range, expr->ino); return expr; } -/** Optimize -size. */ -static struct bfs_expr *optimize_size(struct bfs_opt *opt, struct bfs_expr *expr) { +/** Transfer function for -size. */ +static struct bfs_expr *data_flow_size(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { struct df_range *range = &opt->after_true.ranges[SIZE_RANGE]; if (range->min == range->max) { expr->probability = 0.01; @@ -854,473 +1683,449 @@ static struct bfs_expr *optimize_size(struct bfs_opt *opt, struct bfs_expr *expr return expr; } -/** Estimate probability for -x?type. */ -static void estimate_type_probability(struct bfs_expr *expr) { - unsigned int types = expr->num; - - expr->probability = 0.0; - if (types & (1 << BFS_BLK)) { - expr->probability += 0.00000721183; - } - if (types & (1 << BFS_CHR)) { - expr->probability += 0.0000499855; - } - if (types & (1 << BFS_DIR)) { - expr->probability += 0.114475; - } - if (types & (1 << BFS_DOOR)) { - expr->probability += 0.000001; - } - if (types & (1 << BFS_FIFO)) { - expr->probability += 0.00000248684; - } - if (types & (1 << BFS_REG)) { - expr->probability += 0.859772; - } - if (types & (1 << BFS_LNK)) { - expr->probability += 0.0256816; - } - if (types & (1 << BFS_SOCK)) { - expr->probability += 0.0000116881; - } - if (types & (1 << BFS_WHT)) { - expr->probability += 0.000001; - } -} - -/** Optimize -type. */ -static struct bfs_expr *optimize_type(struct bfs_opt *opt, struct bfs_expr *expr) { +/** Transfer function for -type. */ +static struct bfs_expr *data_flow_type(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { opt->after_true.types &= expr->num; opt->after_false.types &= ~expr->num; - - estimate_type_probability(expr); - return expr; } -/** Optimize -xtype. */ -static struct bfs_expr *optimize_xtype(struct bfs_opt *opt, struct bfs_expr *expr) { - if (opt->ctx->optlevel >= 4) { - // Since -xtype dereferences symbolic links, it may have side - // effects such as reporting permission errors, and thus - // shouldn't be re-ordered without aggressive optimizations - expr->pure = true; +/** Transfer function for -uid. */ +static struct bfs_expr *data_flow_uid(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct df_range *range = &opt->after_true.ranges[UID_RANGE]; + if (range->min == range->max) { + uid_t uid = range->min; + bool nouser = !bfs_getpwuid(opt->ctx->users, uid); + if (errno == 0) { + data_flow_pred(opt, NOUSER_PRED, nouser); + } } + return expr; +} + +/** Transfer function for -xtype. */ +static struct bfs_expr *data_flow_xtype(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { opt->after_true.xtypes &= expr->num; opt->after_false.xtypes &= ~expr->num; - - estimate_type_probability(expr); - return expr; } -/** - * Table of pure expressions. - */ -static bfs_eval_fn *const opt_pure[] = { - eval_access, - eval_acl, - eval_capable, - eval_depth, - eval_false, - eval_flags, - eval_fstype, - eval_gid, - eval_hidden, - eval_inum, - eval_links, - eval_lname, - eval_name, - eval_newer, - eval_nogroup, - eval_nouser, - eval_path, - eval_perm, - eval_regex, - eval_samefile, - eval_size, - eval_sparse, - eval_time, - eval_true, - eval_type, - eval_uid, - eval_used, - eval_xattr, - eval_xattrname, -}; - -/** - * Table of always-true expressions. - */ -static bfs_eval_fn *const opt_always_true[] = { - eval_fls, - eval_fprint, - eval_fprint0, - eval_fprintf, - eval_fprintx, - eval_prune, - eval_true, - - // Non-returning - eval_exit, - eval_quit, -}; - -/** - * Table of always-false expressions. - */ -static bfs_eval_fn *const opt_always_false[] = { - eval_false, - - // Non-returning - eval_exit, - eval_quit, -}; - -#define FAST_COST 40.0 -#define FNMATCH_COST 400.0 -#define STAT_COST 1000.0 -#define PRINT_COST 20000.0 - -/** - * Table of expression costs. - */ -static const struct { - /** The evaluation function with this cost. */ - bfs_eval_fn *eval_fn; - /** The matching cost. */ - float cost; -} opt_costs[] = { - {eval_access, STAT_COST}, - {eval_acl, STAT_COST}, - {eval_capable, STAT_COST}, - {eval_empty, 2 * STAT_COST}, // readdir() is worse than stat() - {eval_fls, PRINT_COST}, - {eval_fprint, PRINT_COST}, - {eval_fprint0, PRINT_COST}, - {eval_fprintf, PRINT_COST}, - {eval_fprintx, PRINT_COST}, - {eval_fstype, STAT_COST}, - {eval_gid, STAT_COST}, - {eval_inum, STAT_COST}, - {eval_links, STAT_COST}, - {eval_lname, FNMATCH_COST}, - {eval_name, FNMATCH_COST}, - {eval_newer, STAT_COST}, - {eval_nogroup, STAT_COST}, - {eval_nouser, STAT_COST}, - {eval_path, FNMATCH_COST}, - {eval_perm, STAT_COST}, - {eval_samefile, STAT_COST}, - {eval_size, STAT_COST}, - {eval_sparse, STAT_COST}, - {eval_time, STAT_COST}, - {eval_uid, STAT_COST}, - {eval_used, STAT_COST}, - {eval_xattr, STAT_COST}, - {eval_xattrname, STAT_COST}, -}; - -/** - * Table of expression probabilities. - */ -static const struct { - /** The evaluation function with this cost. */ - bfs_eval_fn *eval_fn; - /** The matching probability. */ - float probability; -} opt_probs[] = { - {eval_acl, 0.00002}, - {eval_capable, 0.000002}, - {eval_empty, 0.01}, - {eval_false, 0.0}, - {eval_hidden, 0.01}, - {eval_nogroup, 0.01}, - {eval_nouser, 0.01}, - {eval_samefile, 0.01}, - {eval_true, 1.0}, - {eval_xattr, 0.01}, - {eval_xattrname, 0.01}, -}; +/** Data flow visitor entry. */ +static struct bfs_expr *data_flow_enter(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + visit_enter(opt, expr, visitor); -/** - * Table of simple predicates. - */ -static const struct { - /** The evaluation function this optimizer applies to. */ - bfs_eval_fn *eval_fn; - /** The corresponding predicate. */ - enum pred_type pred; -} opt_preds[] = { - {eval_acl, ACL_PRED}, - {eval_capable, CAPABLE_PRED}, - {eval_empty, EMPTY_PRED}, - {eval_hidden, HIDDEN_PRED}, - {eval_nogroup, NOGROUP_PRED}, - {eval_nouser, NOUSER_PRED}, - {eval_sparse, SPARSE_PRED}, - {eval_xattr, XATTR_PRED}, -}; + df_dump(opt, "before", &opt->before); -/** - * Table of simple range comparisons. - */ -static const struct { - /** The evaluation function this optimizer applies to. */ - bfs_eval_fn *eval_fn; - /** The corresponding range. */ - enum range_type range; -} opt_ranges[] = { - {eval_depth, DEPTH_RANGE}, - {eval_gid, GID_RANGE}, - {eval_inum, INUM_RANGE}, - {eval_links, LINKS_RANGE}, - {eval_size, SIZE_RANGE}, - {eval_uid, UID_RANGE}, -}; + if (!bfs_expr_is_parent(expr) && !expr->pure) { + df_join(opt->impure, &opt->before); + df_dump(opt, "impure", opt->impure); + } -/** Signature for custom optimizer functions. */ -typedef struct bfs_expr *bfs_opt_fn(struct bfs_opt *opt, struct bfs_expr *expr); + return expr; +} -/** Table of custom optimizer functions. */ -static const struct { - /** The evaluation function this optimizer applies to. */ - bfs_eval_fn *eval_fn; - /** The corresponding optimizer function. */ - bfs_opt_fn *opt_fn; -} opt_fns[] = { - // Primaries - {eval_access, optimize_access}, - {eval_empty, optimize_empty}, - {eval_exec, optimize_exec}, - {eval_gid, optimize_gid}, - {eval_inum, optimize_inum}, - {eval_links, optimize_links}, - {eval_lname, optimize_fnmatch}, - {eval_name, optimize_fnmatch}, - {eval_path, optimize_fnmatch}, - {eval_samefile, optimize_samefile}, - {eval_size, optimize_size}, - {eval_type, optimize_type}, - {eval_uid, optimize_uid}, - {eval_xtype, optimize_xtype}, - - // Operators - {eval_and, optimize_and_expr_recursive}, - {eval_comma, optimize_comma_expr_recursive}, - {eval_not, optimize_not_expr_recursive}, - {eval_or, optimize_or_expr_recursive}, -}; +/** Data flow visitor exit. */ +static struct bfs_expr *data_flow_leave(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (expr->always_true) { + expr->probability = 1.0; + df_init_bottom(&opt->after_false); + } -/** - * Look up the appropriate optimizer for an expression and call it. - */ -static struct bfs_expr *optimize_expr_lookup(struct bfs_opt *opt, struct bfs_expr *expr) { - for (size_t i = 0; i < countof(opt_pure); ++i) { - if (opt_pure[i] == expr->eval_fn) { - expr->pure = true; - break; - } + if (expr->always_false) { + expr->probability = 0.0; + df_init_bottom(&opt->after_true); } - for (size_t i = 0; i < countof(opt_always_true); ++i) { - if (opt_always_true[i] == expr->eval_fn) { + df_dump(opt, "after true", &opt->after_true); + df_dump(opt, "after false", &opt->after_false); + + if (df_is_bottom(&opt->after_false)) { + if (!expr->pure) { expr->always_true = true; - break; + expr->probability = 0.0; + } else if (expr->eval_fn != eval_true) { + opt_warning(opt, expr, "This expression is always true.\n\n"); + opt_debug(opt, "pure, always true\n"); + expr = opt_const(opt, true); + if (!expr) { + return NULL; + } } } - for (size_t i = 0; i < countof(opt_always_false); ++i) { - if (opt_always_false[i] == expr->eval_fn) { + if (df_is_bottom(&opt->after_true)) { + if (!expr->pure) { expr->always_false = true; - break; + expr->probability = 0.0; + } else if (expr->eval_fn != eval_false) { + opt_warning(opt, expr, "This expression is always false.\n\n"); + opt_debug(opt, "pure, always false\n"); + expr = opt_const(opt, false); + if (!expr) { + return NULL; + } } } - expr->cost = FAST_COST; - for (size_t i = 0; i < countof(opt_costs); ++i) { - if (opt_costs[i].eval_fn == expr->eval_fn) { - expr->cost = opt_costs[i].cost; - break; + return visit_leave(opt, expr, visitor); +} + +/** Data flow visitor function. */ +static struct bfs_expr *data_flow_visit(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (opt->ignore_result && expr->pure) { + opt_debug(opt, "ignored result\n"); + opt_warning(opt, expr, "The result of this expression is ignored.\n\n"); + expr = opt_const(opt, false); + if (!expr) { + return NULL; } } - for (size_t i = 0; i < countof(opt_probs); ++i) { - if (opt_probs[i].eval_fn == expr->eval_fn) { - expr->probability = opt_probs[i].probability; - break; + if (df_is_bottom(&opt->before)) { + opt_debug(opt, "unreachable\n"); + opt_warning(opt, expr, "This expression is unreachable.\n\n"); + expr = opt_const(opt, false); + if (!expr) { + return NULL; } } - for (size_t i = 0; i < countof(opt_preds); ++i) { - if (opt_preds[i].eval_fn == expr->eval_fn) { - opt_constrain_pred(opt, opt_preds[i].pred, true); + /** Table of simple predicates. */ + static const struct { + bfs_eval_fn *eval_fn; + enum pred_type pred; + } preds[] = { + {eval_acl, ACL_PRED}, + {eval_capable, CAPABLE_PRED}, + {eval_empty, EMPTY_PRED}, + {eval_hidden, HIDDEN_PRED}, + {eval_nogroup, NOGROUP_PRED}, + {eval_nouser, NOUSER_PRED}, + {eval_sparse, SPARSE_PRED}, + {eval_xattr, XATTR_PRED}, + }; + + for (size_t i = 0; i < countof(preds); ++i) { + if (preds[i].eval_fn == expr->eval_fn) { + data_flow_pred(opt, preds[i].pred, true); break; } } - for (size_t i = 0; i < countof(opt_ranges); ++i) { - if (opt_ranges[i].eval_fn == expr->eval_fn) { - optimize_icmp(opt, expr, opt_ranges[i].range); + /** Table of simple range comparisons. */ + static const struct { + bfs_eval_fn *eval_fn; + enum range_type range; + } ranges[] = { + {eval_depth, DEPTH_RANGE}, + {eval_gid, GID_RANGE}, + {eval_inum, INUM_RANGE}, + {eval_links, LINKS_RANGE}, + {eval_size, SIZE_RANGE}, + {eval_uid, UID_RANGE}, + }; + + for (size_t i = 0; i < countof(ranges); ++i) { + if (ranges[i].eval_fn == expr->eval_fn) { + data_flow_icmp(opt, expr, ranges[i].range); break; } } - for (size_t i = 0; i < countof(opt_fns); ++i) { - if (opt_fns[i].eval_fn == expr->eval_fn) { - return opt_fns[i].opt_fn(opt, expr); - } + return expr; +} + +/** + * Data flow visitor. + */ +static const struct visitor data_flow = { + .name = "data_flow", + .enter = data_flow_enter, + .visit = data_flow_visit, + .leave = data_flow_leave, + .table = { + {eval_access, data_flow_access}, + {eval_gid, data_flow_gid}, + {eval_inum, data_flow_inum}, + {eval_links, data_flow_links}, + {eval_samefile, data_flow_samefile}, + {eval_size, data_flow_size}, + {eval_type, data_flow_type}, + {eval_uid, data_flow_uid}, + {eval_xtype, data_flow_xtype}, + {NULL, NULL}, + }, +}; + +/** Simplify a negation. */ +static struct bfs_expr *simplify_not(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (opt->ignore_result) { + opt_debug(opt, "ignored result\n"); + expr = only_child(expr); } return expr; } -static struct bfs_expr *optimize_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { - int optlevel = opt->ctx->optlevel; +/** Lift negations out of a conjunction/disjunction using De Morgan's laws. */ +static struct bfs_expr *lift_andor_not(struct bfs_opt *opt, struct bfs_expr *expr) { + // Only lift negations if it would reduce the number of (-not) expressions + size_t added = 0, removed = 0; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + if (child->eval_fn == eval_not) { + ++removed; + } else { + ++added; + } + } + if (added >= removed) { + return visit_shallow(opt, expr, &annotate); + } - opt->after_true = opt->before; - opt->after_false = opt->before; + opt_debug(opt, "De Morgan's laws\n"); - if (optlevel >= 2 && df_is_bottom(&opt->before)) { - struct bfs_expr *ret = opt_const(opt, false); - opt_debug(opt, 2, "reachability: %pe --> %pe\n", expr, ret); - opt_warning(opt, expr, "This expression is unreachable.\n\n"); - return ret; + if (expr->eval_fn == eval_and) { + expr->eval_fn = eval_or; + expr->argv = &fake_or_arg; + } else { + bfs_assert(expr->eval_fn == eval_or); + expr->eval_fn = eval_and; + expr->argv = &fake_and_arg; + } + + struct bfs_exprs children; + foster_children(expr, &children); + + struct bfs_expr *child; + while ((child = SLIST_POP(&children))) { + opt_enter(opt, "%pe\n", child); + + child = negate_expr(opt, child, &fake_not_arg); + if (!child) { + return NULL; + } + + opt_leave(opt, "%pe\n", child); + bfs_expr_append(expr, child); } - expr = optimize_expr_lookup(opt, expr); - if (!expr) { + expr = visit_shallow(opt, expr, &annotate); + return negate_expr(opt, expr, &fake_not_arg); +} + +/** Get the first ignorable expression in a conjunction/disjunction. */ +static struct bfs_expr *first_ignorable(struct bfs_opt *opt, struct bfs_expr *expr) { + if (opt->level < 2 || !opt->ignore_result) { return NULL; } - if (bfs_expr_is_parent(expr)) { - struct bfs_expr *lhs = expr->lhs; - struct bfs_expr *rhs = expr->rhs; - if (rhs) { - expr->persistent_fds = rhs->persistent_fds; - expr->ephemeral_fds = rhs->ephemeral_fds; + struct bfs_expr *ret = NULL; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + if (!child->pure) { + ret = NULL; + } else if (!ret) { + ret = child; + } + } + + return ret; +} + +/** Simplify a conjunction. */ +static struct bfs_expr *simplify_and(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_expr *ignorable = first_ignorable(opt, expr); + bool ignore = false; + + struct bfs_exprs children; + foster_children(expr, &children); + + while (!SLIST_EMPTY(&children)) { + struct bfs_expr *child = SLIST_POP(&children); + + if (child == ignorable) { + ignore = true; + } + + if (ignore) { + opt_delete(opt, "%pe [ignored result]\n", child); + opt_warning(opt, child, "The result of this expression is ignored.\n\n"); + continue; + } + + if (child->eval_fn == eval_true) { + opt_delete(opt, "%pe [conjunction elimination]\n", child); + continue; } - if (lhs) { - expr->persistent_fds += lhs->persistent_fds; - if (lhs->ephemeral_fds > expr->ephemeral_fds) { - expr->ephemeral_fds = lhs->ephemeral_fds; + + opt_visit(opt, "%pe\n", child); + bfs_expr_append(expr, child); + + if (child->always_false) { + while ((child = SLIST_POP(&children))) { + opt_delete(opt, "%pe [short-circuit]\n", child); } } - } else if (!expr->pure) { - df_join(opt->impure, &opt->before); } - if (expr->always_true) { - expr->probability = 1.0; - df_init_bottom(&opt->after_false); - } - if (expr->always_false) { - expr->probability = 0.0; - df_init_bottom(&opt->after_true); + struct bfs_expr *child = bfs_expr_children(expr); + if (!child) { + opt_debug(opt, "nullary identity\n"); + return opt_const(opt, true); + } else if (!child->next) { + opt_debug(opt, "unary identity\n"); + return only_child(expr); } - if (optlevel < 2 || expr->eval_fn == eval_true || expr->eval_fn == eval_false) { - return expr; - } + return lift_andor_not(opt, expr); +} - if (df_is_bottom(&opt->after_true)) { - if (expr->pure) { - struct bfs_expr *ret = opt_const(opt, false); - opt_warning(opt, expr, "This expression is always false.\n\n"); - opt_debug(opt, 2, "data flow: %pe --> %pe\n", expr, ret); - return ret; - } else { - expr->always_false = true; - expr->probability = 0.0; +/** Simplify a disjunction. */ +static struct bfs_expr *simplify_or(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_expr *ignorable = first_ignorable(opt, expr); + bool ignore = false; + + struct bfs_exprs children; + foster_children(expr, &children); + + while (!SLIST_EMPTY(&children)) { + struct bfs_expr *child = SLIST_POP(&children); + + if (child == ignorable) { + ignore = true; } - } else if (df_is_bottom(&opt->after_false)) { - if (expr->pure) { - struct bfs_expr *ret = opt_const(opt, true); - opt_debug(opt, 2, "data flow: %pe --> %pe\n", expr, ret); - opt_warning(opt, expr, "This expression is always true.\n\n"); - return ret; - } else { - expr->always_true = true; - expr->probability = 1.0; + + if (ignore) { + opt_delete(opt, "%pe [ignored result]\n", child); + opt_warning(opt, child, "The result of this expression is ignored.\n\n"); + continue; } - } - return expr; -} + if (child->eval_fn == eval_false) { + opt_delete(opt, "%pe [disjunctive syllogism]\n", child); + continue; + } -/** Swap the children of a binary expression if it would reduce the cost. */ -static bool reorder_expr(const struct bfs_opt *opt, struct bfs_expr *expr, float swapped_cost) { - if (swapped_cost < expr->cost) { - bool debug = opt_debug(opt, 3, "cost: %pe <==> ", expr); - struct bfs_expr *lhs = expr->lhs; - expr->lhs = expr->rhs; - expr->rhs = lhs; - if (debug) { - cfprintf(opt->ctx->cerr, "%pe (~${ylw}%g${rs} --> ~${ylw}%g${rs})\n", expr, expr->cost, swapped_cost); + opt_visit(opt, "%pe\n", child); + bfs_expr_append(expr, child); + + if (child->always_true) { + while ((child = SLIST_POP(&children))) { + opt_delete(opt, "%pe [short-circuit]\n", child); + } } - expr->cost = swapped_cost; - return true; - } else { - return false; } -} -/** - * Recursively reorder sub-expressions to reduce the overall cost. - * - * @param expr - * The expression to optimize. - * @return - * Whether any subexpression was reordered. - */ -static bool reorder_expr_recursive(const struct bfs_opt *opt, struct bfs_expr *expr) { - if (!bfs_expr_is_parent(expr)) { - return false; + struct bfs_expr *child = bfs_expr_children(expr); + if (!child) { + opt_debug(opt, "nullary identity\n"); + return opt_const(opt, false); + } else if (!child->next) { + opt_debug(opt, "unary identity\n"); + return only_child(expr); } - struct bfs_expr *lhs = expr->lhs; - struct bfs_expr *rhs = expr->rhs; + return lift_andor_not(opt, expr); +} - bool ret = false; - if (lhs) { - ret |= reorder_expr_recursive(opt, lhs); - } - if (rhs) { - ret |= reorder_expr_recursive(opt, rhs); - } +/** Simplify a comma expression. */ +static struct bfs_expr *simplify_comma(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_exprs children; + foster_children(expr, &children); + + while (!SLIST_EMPTY(&children)) { + struct bfs_expr *child = SLIST_POP(&children); - if (expr->eval_fn == eval_and || expr->eval_fn == eval_or) { - if (lhs->pure && rhs->pure) { - float rhs_prob = expr->eval_fn == eval_and ? rhs->probability : 1.0 - rhs->probability; - float swapped_cost = rhs->cost + rhs_prob * lhs->cost; - ret |= reorder_expr(opt, expr, swapped_cost); + if (opt->level >= 2 && child->pure && !SLIST_EMPTY(&children)) { + opt_delete(opt, "%pe [ignored result]\n", child); + opt_warning(opt, child, "The result of this expression is ignored.\n\n"); + continue; } + + opt_visit(opt, "%pe\n", child); + bfs_expr_append(expr, child); } - return ret; + struct bfs_expr *child = bfs_expr_children(expr); + if (child && !child->next) { + opt_debug(opt, "unary identity\n"); + return only_child(expr); + } + + return expr; } /** - * Optimize a top-level expression. + * Logical simplification visitor. */ -static struct bfs_expr *optimize_expr(struct bfs_opt *opt, struct bfs_expr *expr) { - struct df_domain saved_impure = *opt->impure; +static const struct visitor simplify = { + .name = "simplify", + .table = { + {eval_not, simplify_not}, + {eval_and, simplify_and}, + {eval_or, simplify_or}, + {eval_comma, simplify_comma}, + {NULL, NULL}, + }, +}; - expr = optimize_expr_recursive(opt, expr); - if (!expr) { - return NULL; - } +/** Optimize an expression. */ +static struct bfs_expr *optimize(struct bfs_opt *opt, struct bfs_expr *expr) { + opt_enter(opt, "pass 0:\n"); + expr = visit(opt, expr, &annotate); + opt_leave(opt, NULL); + + /** Table of optimization passes. */ + static const struct { + /** Minimum optlevel for this pass. */ + int level; + /** The visitor for this pass. */ + const struct visitor *visitor; + } passes[] = { + {1, &canonicalize}, + {3, &reorder}, + {2, &data_flow}, + {1, &simplify}, + }; - if (opt->ctx->optlevel >= 3 && reorder_expr_recursive(opt, expr)) { - // Re-do optimizations to account for the new ordering - *opt->impure = saved_impure; - expr = optimize_expr_recursive(opt, expr); - if (!expr) { - return NULL; + struct df_domain impure; + + for (int i = 0; i < 3; ++i) { + struct bfs_opt nested = *opt; + nested.impure = &impure; + impure = *opt->impure; + + opt_enter(&nested, "pass %d:\n", i + 1); + + for (size_t j = 0; j < countof(passes); ++j) { + if (opt->level < passes[j].level) { + continue; + } + + // Skip reordering the first time through the passes, to + // make warnings more understandable + if (passes[j].visitor == &reorder) { + if (i == 0) { + continue; + } else { + nested.warn = false; + } + } + + expr = visit(&nested, expr, passes[j].visitor); + if (!expr) { + return NULL; + } + } + + opt_leave(&nested, NULL); + + if (!bfs_expr_is_parent(expr)) { + break; } } + *opt->impure = impure; return expr; } @@ -1332,30 +2137,37 @@ int bfs_optimize(struct bfs_ctx *ctx) { struct bfs_opt opt = { .ctx = ctx, + .level = ctx->optlevel, + .depth = 0, + .warn = ctx->warn, + .ignore_result = false, .impure = &impure, }; df_init_top(&opt.before); - ctx->exclude = optimize_expr(&opt, ctx->exclude); + ctx->exclude = optimize(&opt, ctx->exclude); if (!ctx->exclude) { return -1; } // Only non-excluded files are evaluated opt.before = opt.after_false; + opt.ignore_result = true; struct df_range *depth = &opt.before.ranges[DEPTH_RANGE]; - constrain_min(depth, ctx->mindepth); - constrain_max(depth, ctx->maxdepth); + if (ctx->mindepth > 0) { + constrain_min(depth, ctx->mindepth); + } + if (ctx->maxdepth < INT_MAX) { + constrain_max(depth, ctx->maxdepth); + } - ctx->expr = optimize_expr(&opt, ctx->expr); + ctx->expr = optimize(&opt, ctx->expr); if (!ctx->expr) { return -1; } - ctx->expr = ignore_result(&opt, ctx->expr); - - if (df_is_bottom(&impure)) { + if (opt.level >= 2 && df_is_bottom(&impure)) { bfs_warning(ctx, "This command won't do anything.\n\n"); } @@ -1363,23 +2175,27 @@ int bfs_optimize(struct bfs_ctx *ctx) { long long mindepth = impure_depth->min; long long maxdepth = impure_depth->max; - int optlevel = ctx->optlevel; + opt_enter(&opt, "post-process:\n"); - if (optlevel >= 2 && mindepth > ctx->mindepth) { + if (opt.level >= 2 && mindepth > ctx->mindepth) { if (mindepth > INT_MAX) { mindepth = INT_MAX; } + opt_enter(&opt, "${blu}-mindepth${rs} ${bld}%d${rs}\n", ctx->mindepth); ctx->mindepth = mindepth; - opt_debug(&opt, 2, "data flow: mindepth --> %d\n", ctx->mindepth); + opt_leave(&opt, "${blu}-mindepth${rs} ${bld}%d${rs}\n", ctx->mindepth); } - if (optlevel >= 4 && maxdepth < ctx->maxdepth) { + if (opt.level >= 4 && maxdepth < ctx->maxdepth) { if (maxdepth < INT_MIN) { maxdepth = INT_MIN; } + opt_enter(&opt, "${blu}-maxdepth${rs} ${bld}%d${rs}\n", ctx->maxdepth); ctx->maxdepth = maxdepth; - opt_debug(&opt, 4, "data flow: maxdepth --> %d\n", ctx->maxdepth); + opt_leave(&opt, "${blu}-maxdepth${rs} ${bld}%d${rs}\n", ctx->maxdepth); } + opt_leave(&opt, NULL); + return 0; } diff --git a/src/parse.c b/src/parse.c index 17fe8ad..4212196 100644 --- a/src/parse.c +++ b/src/parse.c @@ -21,6 +21,7 @@ #include "exec.h" #include "expr.h" #include "fsade.h" +#include "list.h" #include "opt.h" #include "printf.h" #include "pwcache.h" @@ -47,10 +48,9 @@ #include // Strings printed by -D tree for "fake" expressions -static char *fake_and_arg = "-a"; -static char *fake_false_arg = "-false"; +static char *fake_and_arg = "-and"; static char *fake_hidden_arg = "-hidden"; -static char *fake_or_arg = "-o"; +static char *fake_or_arg = "-or"; static char *fake_print_arg = "-print"; static char *fake_true_arg = "-true"; @@ -319,12 +319,8 @@ static struct bfs_expr *new_unary_expr(const struct bfs_parser *parser, bfs_eval return NULL; } - expr->lhs = NULL; - expr->rhs = rhs; bfs_assert(bfs_expr_is_parent(expr)); - - expr->persistent_fds = rhs->persistent_fds; - expr->ephemeral_fds = rhs->ephemeral_fds; + bfs_expr_append(expr, rhs); return expr; } @@ -337,17 +333,9 @@ static struct bfs_expr *new_binary_expr(const struct bfs_parser *parser, bfs_eva return NULL; } - expr->lhs = lhs; - expr->rhs = rhs; bfs_assert(bfs_expr_is_parent(expr)); - - expr->persistent_fds = lhs->persistent_fds + rhs->persistent_fds; - if (lhs->ephemeral_fds > rhs->ephemeral_fds) { - expr->ephemeral_fds = lhs->ephemeral_fds; - } else { - expr->ephemeral_fds = rhs->ephemeral_fds; - } - + bfs_expr_append(expr, lhs); + bfs_expr_append(expr, rhs); return expr; } @@ -774,19 +762,6 @@ static struct bfs_expr *parse_unary_action(struct bfs_parser *parser, bfs_eval_f return parse_action(parser, eval_fn, 2); } -/** - * Add an expression to the exclusions. - */ -static int parse_exclude(struct bfs_parser *parser, struct bfs_expr *expr) { - struct bfs_ctx *ctx = parser->ctx; - ctx->exclude = new_binary_expr(parser, eval_or, ctx->exclude, expr, &fake_or_arg); - if (ctx->exclude) { - return 0; - } else { - return -1; - } -} - /** * Parse a test expression with integer data and a comparison flag. */ @@ -1839,10 +1814,7 @@ static struct bfs_expr *parse_nohidden(struct bfs_parser *parser, int arg1, int return NULL; } - if (parse_exclude(parser, hidden) != 0) { - return NULL; - } - + bfs_expr_append(parser->ctx->exclude, hidden); return parse_nullary_option(parser); } @@ -3206,10 +3178,7 @@ static struct bfs_expr *parse_factor(struct bfs_parser *parser) { parser->excluding = false; - if (parse_exclude(parser, factor) != 0) { - return NULL; - } - + bfs_expr_append(parser->ctx->exclude, factor); return parse_new_expr(parser, eval_true, parser->argv - argv, argv); } else if (strcmp(arg, "!") == 0 || strcmp(arg, "-not") == 0) { char **argv = parser_advance(parser, T_OPERATOR, 1); @@ -3428,19 +3397,29 @@ static void dump_expr_multiline(const struct bfs_ctx *ctx, enum debug_flags flag cfprintf(ctx->cerr, " "); } + bool close = true; + if (bfs_expr_is_parent(expr)) { - cfprintf(ctx->cerr, "(${red}%s${rs}\n", expr->argv[0]); - if (expr->lhs) { - dump_expr_multiline(ctx, flag, expr->lhs, indent + 1, 0); + if (SLIST_EMPTY(&expr->children)) { + cfprintf(ctx->cerr, "(${red}%s${rs}", expr->argv[0]); + ++rparens; + } else { + cfprintf(ctx->cerr, "(${red}%s${rs}\n", expr->argv[0]); + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + int parens = child->next ? 0 : rparens + 1; + dump_expr_multiline(ctx, flag, child, indent + 1, parens); + } + close = false; } - dump_expr_multiline(ctx, flag, expr->rhs, indent + 1, rparens + 1); } else { if (flag == DEBUG_RATES) { cfprintf(ctx->cerr, "%pE", expr); } else { cfprintf(ctx->cerr, "%pe", expr); } + } + if (close) { for (int i = 0; i < rparens; ++i) { cfprintf(ctx->cerr, ")"); } @@ -3540,10 +3519,8 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) { fputs("\n", stderr); - if (ctx->exclude->eval_fn != eval_false) { - bfs_debug(ctx, flag, "(${red}-exclude${rs}\n"); - dump_expr_multiline(ctx, flag, ctx->exclude, 1, 1); - } + bfs_debug(ctx, flag, "(${red}-exclude${rs}\n"); + dump_expr_multiline(ctx, flag, ctx->exclude, 1, 1); dump_expr_multiline(ctx, flag, ctx->expr, 0, 0); } @@ -3638,7 +3615,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { .now = ctx->now, }; - ctx->exclude = parse_new_expr(&parser, eval_false, 1, &fake_false_arg); + ctx->exclude = parse_new_expr(&parser, eval_or, 1, &fake_or_arg); if (!ctx->exclude) { goto fail; } -- cgit v1.2.3 From 6bb323d446e2500c5a20866b56335ac8633e1c23 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 5 Feb 2024 14:20:02 -0500 Subject: ctx: Fill in ctx->threads earlier --- src/ctx.c | 16 ++++++++++++++++ src/eval.c | 21 ++------------------- src/parse.c | 6 ++---- 3 files changed, 20 insertions(+), 23 deletions(-) (limited to 'src/ctx.c') diff --git a/src/ctx.c b/src/ctx.c index 1f3e72e..6c84f75 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -15,6 +15,21 @@ #include #include #include +#include + +/** Get the initial value for ctx->threads (-j). */ +static int bfs_nproc(void) { + long nproc = sysconf(_SC_NPROCESSORS_ONLN); + + if (nproc < 1) { + nproc = 1; + } else if (nproc > 8) { + // Not much speedup after 8 threads + nproc = 8; + } + + return nproc; +} struct bfs_ctx *bfs_ctx_new(void) { struct bfs_ctx *ctx = ZALLOC(struct bfs_ctx); @@ -28,6 +43,7 @@ struct bfs_ctx *bfs_ctx_new(void) { ctx->maxdepth = INT_MAX; ctx->flags = BFTW_RECOVER; ctx->strategy = BFTW_BFS; + ctx->threads = bfs_nproc(); ctx->optlevel = 3; trie_init(&ctx->files); diff --git a/src/eval.c b/src/eval.c index dfeaa1e..a4c0c11 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1515,19 +1515,6 @@ done: return ret; } -static int infer_nproc(void) { - long nproc = sysconf(_SC_NPROCESSORS_ONLN); - - if (nproc < 1) { - nproc = 1; - } else if (nproc > 8) { - // Not much speedup after 8 threads - nproc = 8; - } - - return nproc; -} - /** * Dump the bftw() flags for -D search. */ @@ -1614,12 +1601,8 @@ int bfs_eval(struct bfs_ctx *ctx) { reserve_fds(fdlimit); fdlimit = infer_fdlimit(ctx, fdlimit); - int nthreads; - if (ctx->threads > 0) { - nthreads = ctx->threads - 1; - } else { - nthreads = infer_nproc() - 1; - } + // -1 for the main thread + int nthreads = ctx->threads - 1; struct bftw_args bftw_args = { .paths = ctx->paths, diff --git a/src/parse.c b/src/parse.c index 4212196..3a78840 100644 --- a/src/parse.c +++ b/src/parse.c @@ -3452,14 +3452,12 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) { cfprintf(cerr, " ${cyn}-s${rs}"); } + cfprintf(cerr, " ${cyn}-j${bld}%d${rs}", ctx->threads); + if (ctx->optlevel != 3) { cfprintf(cerr, " ${cyn}-O${bld}%d${rs}", ctx->optlevel); } - if (ctx->threads > 0) { - cfprintf(cerr, " ${cyn}-j${bld}%d${rs}", ctx->threads); - } - cfprintf(cerr, " ${cyn}-S${rs} ${bld}%s${rs}", bftw_strategy_name(ctx->strategy)); enum debug_flags debug = ctx->debug; -- cgit v1.2.3 From 2c3ef3a06ee1f951f6d68be6d0d3f6a1822b05b7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 11 Mar 2024 09:51:03 -0400 Subject: Re-run include-what-you-use --- src/alloc.h | 1 + src/bfstd.c | 6 +++--- src/bftw.c | 1 + src/color.h | 1 - src/ctx.c | 1 + src/ctx.h | 2 ++ src/diag.c | 2 +- src/dir.h | 4 ++-- src/dstring.c | 2 ++ src/eval.c | 4 ++-- src/expr.c | 4 ++-- src/expr.h | 1 - src/ioq.c | 5 +++-- src/main.c | 1 + src/opt.c | 3 ++- src/parse.c | 3 +-- src/printf.c | 3 ++- src/trie.c | 2 -- src/trie.h | 1 - src/xspawn.c | 1 - src/xtime.c | 1 - tests/alloc.c | 1 + tests/bfstd.c | 3 --- tests/bit.c | 2 +- tests/ioq.c | 2 ++ tests/main.c | 3 --- tests/xtime.c | 4 ++-- tests/xtouch.c | 1 + 28 files changed, 33 insertions(+), 32 deletions(-) (limited to 'src/ctx.c') diff --git a/src/alloc.h b/src/alloc.h index 60dd738..ae055bc 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -10,6 +10,7 @@ #include "config.h" #include +#include #include /** Check if a size is properly aligned. */ diff --git a/src/bfstd.c b/src/bfstd.c index ce4aa49..c6c2e7f 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -2,18 +2,19 @@ // SPDX-License-Identifier: 0BSD #include "bfstd.h" -#include "bit.h" #include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" #include "xregex.h" -#include #include #include #include +#include #include #include +#include +#include #include #include #include @@ -24,7 +25,6 @@ #include #include #include -#include #if BFS_USE_SYS_SYSMACROS_H # include diff --git a/src/bftw.c b/src/bftw.c index 6f52299..50b8b02 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -35,6 +35,7 @@ #include #include #include +#include /** Initialize a bftw_stat cache. */ static void bftw_stat_init(struct bftw_stat *bufs, struct bfs_stat *stat_buf, struct bfs_stat *lstat_buf) { diff --git a/src/color.h b/src/color.h index 85633a4..e3e7973 100644 --- a/src/color.h +++ b/src/color.h @@ -10,7 +10,6 @@ #include "config.h" #include "dstring.h" -#include #include /** diff --git a/src/ctx.c b/src/ctx.c index 6c84f75..f5b28c7 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -6,6 +6,7 @@ #include "color.h" #include "diag.h" #include "expr.h" +#include "list.h" #include "mtab.h" #include "pwcache.h" #include "stat.h" diff --git a/src/ctx.h b/src/ctx.h index aa91f2c..e14db21 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -18,6 +18,8 @@ #include #include +struct CFILE; + /** * The execution context for bfs. */ diff --git a/src/diag.c b/src/diag.c index 656fa89..cb27b92 100644 --- a/src/diag.c +++ b/src/diag.c @@ -11,8 +11,8 @@ #include "expr.h" #include #include +#include #include -#include /** bfs_diagf() implementation. */ attr(printf(2, 0)) diff --git a/src/dir.h b/src/dir.h index b11d454..18d907e 100644 --- a/src/dir.h +++ b/src/dir.h @@ -8,8 +8,6 @@ #ifndef BFS_DIR_H #define BFS_DIR_H -#include "alloc.h" -#include "config.h" #include /** @@ -78,6 +76,8 @@ struct bfs_dirent { */ struct bfs_dir *bfs_allocdir(void); +struct arena; + /** * Initialize an arena for directories. * diff --git a/src/dstring.c b/src/dstring.c index bc18308..10b0fad 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -7,6 +7,8 @@ #include "config.h" #include "diag.h" #include +#include +#include #include #include #include diff --git a/src/eval.c b/src/eval.c index 1711001..9e55964 100644 --- a/src/eval.c +++ b/src/eval.c @@ -25,7 +25,6 @@ #include "stat.h" #include "trie.h" #include "xregex.h" -#include "xtime.h" #include #include #include @@ -36,8 +35,9 @@ #include #include #include +#include #include -#include +#include #include #include #include diff --git a/src/expr.c b/src/expr.c index 3e0033f..5784220 100644 --- a/src/expr.c +++ b/src/expr.c @@ -4,12 +4,12 @@ #include "expr.h" #include "alloc.h" #include "ctx.h" +#include "diag.h" #include "eval.h" #include "exec.h" +#include "list.h" #include "printf.h" #include "xregex.h" -#include -#include #include struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval_fn, size_t argc, char **argv) { diff --git a/src/expr.h b/src/expr.h index 957b04a..75cb5fd 100644 --- a/src/expr.h +++ b/src/expr.h @@ -12,7 +12,6 @@ #include "config.h" #include "eval.h" #include "stat.h" -#include #include #include diff --git a/src/ioq.c b/src/ioq.c index 00c3b86..37eed7d 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -126,13 +126,14 @@ #include "config.h" #include "diag.h" #include "dir.h" -#include "sanity.h" #include "stat.h" #include "thread.h" -#include #include +#include #include +#include #include +#include #if BFS_USE_LIBURING # include diff --git a/src/main.c b/src/main.c index 16a2576..e120f03 100644 --- a/src/main.c +++ b/src/main.c @@ -48,6 +48,7 @@ #include "bfstd.h" #include "config.h" #include "ctx.h" +#include "diag.h" #include "eval.h" #include "parse.h" #include diff --git a/src/opt.c b/src/opt.c index 74145ac..76965de 100644 --- a/src/opt.c +++ b/src/opt.c @@ -26,11 +26,13 @@ */ #include "opt.h" +#include "bftw.h" #include "bit.h" #include "color.h" #include "config.h" #include "ctx.h" #include "diag.h" +#include "dir.h" #include "eval.h" #include "exec.h" #include "expr.h" @@ -40,7 +42,6 @@ #include #include #include -#include #include static char *fake_and_arg = "-and"; diff --git a/src/parse.c b/src/parse.c index b26a50f..3b7386d 100644 --- a/src/parse.c +++ b/src/parse.c @@ -42,8 +42,7 @@ #include #include #include -#include -#include +#include #include #include diff --git a/src/printf.c b/src/printf.c index 34ed606..487f039 100644 --- a/src/printf.c +++ b/src/printf.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "printf.h" +#include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" @@ -14,10 +15,10 @@ #include "mtab.h" #include "pwcache.h" #include "stat.h" -#include "xtime.h" #include #include #include +#include #include #include #include diff --git a/src/trie.c b/src/trie.c index bd5300d..1ffb23a 100644 --- a/src/trie.c +++ b/src/trie.c @@ -87,9 +87,7 @@ #include "config.h" #include "diag.h" #include "list.h" -#include #include -#include #include bfs_static_assert(CHAR_WIDTH == 8); diff --git a/src/trie.h b/src/trie.h index 02088f1..4288d76 100644 --- a/src/trie.h +++ b/src/trie.h @@ -5,7 +5,6 @@ #define BFS_TRIE_H #include "alloc.h" -#include "config.h" #include "list.h" #include #include diff --git a/src/xspawn.c b/src/xspawn.c index 8d6108b..6a94d3d 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #if BFS_USE_PATHS_H diff --git a/src/xtime.c b/src/xtime.c index 05f0e1a..5b259ab 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -7,7 +7,6 @@ #include "diag.h" #include #include -#include #include #include #include diff --git a/tests/alloc.c b/tests/alloc.c index 4ce23d4..9f08111 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -3,6 +3,7 @@ #include "tests.h" #include "../src/alloc.h" +#include "../src/config.h" #include "../src/diag.h" #include #include diff --git a/tests/bfstd.c b/tests/bfstd.c index 0ded5de..26abdb6 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -7,9 +7,6 @@ #include "../src/diag.h" #include #include -#include -#include -#include #include #include diff --git a/tests/bit.c b/tests/bit.c index b944748..3d66ce3 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -3,10 +3,10 @@ #include "tests.h" #include "../src/bit.h" +#include "../src/config.h" #include "../src/diag.h" #include #include -#include bfs_static_assert(UMAX_WIDTH(0x1) == 1); bfs_static_assert(UMAX_WIDTH(0x3) == 2); diff --git a/tests/ioq.c b/tests/ioq.c index 56e1886..1ce8f75 100644 --- a/tests/ioq.c +++ b/tests/ioq.c @@ -4,10 +4,12 @@ #include "tests.h" #include "../src/ioq.h" #include "../src/bfstd.h" +#include "../src/config.h" #include "../src/diag.h" #include "../src/dir.h" #include #include +#include /** * Test for blocking within ioq_slot_push(). diff --git a/tests/main.c b/tests/main.c index 38438b2..8849e8c 100644 --- a/tests/main.c +++ b/tests/main.c @@ -6,11 +6,8 @@ */ #include "tests.h" -#include "../src/bfstd.h" #include "../src/color.h" #include "../src/config.h" -#include "../src/diag.h" -#include #include #include #include diff --git a/tests/xtime.c b/tests/xtime.c index 3f1fec2..c8dc00b 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -5,10 +5,10 @@ #include "../src/xtime.h" #include "../src/bfstd.h" #include "../src/config.h" +#include "../src/diag.h" #include +#include #include -#include -#include #include static bool tm_equal(const struct tm *tma, const struct tm *tmb) { diff --git a/tests/xtouch.c b/tests/xtouch.c index 8c5c5f3..fad272f 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From 98a026cf8b9d5da7730d5d4d1c88ba35d94771b2 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 24 Apr 2024 12:06:09 -0400 Subject: ctx: Escape paths when reporting errors in bfs_ctx_free() --- src/ctx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/ctx.c') diff --git a/src/ctx.c b/src/ctx.c index f5b28c7..aa73b35 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -237,7 +237,7 @@ int bfs_ctx_free(struct bfs_ctx *ctx) { if (bfs_ctx_fclose(ctx, ctx_file) != 0) { if (cerr) { - bfs_error(ctx, "'%s': %m.\n", ctx_file->path); + bfs_error(ctx, "%pq: %m.\n", ctx_file->path); } ret = -1; } -- cgit v1.2.3