From 23643d28ec62de48e4a42815eead078ad727be4b Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Fri, 28 Aug 2026 17:46:06 -0700 Subject: [PATCH] csh: new package Spack has a `tcsh` package but not a `csh` package. There are systems wtih real `csh` and we try to keep our `setup-env.csh` compatible with them, so add `csh` for testing, even if most of the world has moved on. This builds from Debian's csh source package, which is based on the original. We use a lot of Debian's patches and add some for macOS. - [x] Ship a short GNUmakefile, since upstream uses BSD Make. On Linux: - [x] Apply Debian's glibc portability patches. On macOS: - [x] Patch to use macOS libc shims, use in-tree allocators instead of publib, various other portability. This builds on macOS Tahoe and RHEL8. Assisted-By: Claude Signed-off-by: Todd Gamblin --- .../builtin/packages/csh/GNUmakefile | 45 +++++++ .../packages/csh/linux-closefrom.patch | 10 ++ .../builtin/packages/csh/macos-compat.patch | 66 ++++++++++ .../builtin/packages/csh/no-publib.patch | 28 +++++ .../builtin/packages/csh/package.py | 113 ++++++++++++++++++ 5 files changed, 262 insertions(+) create mode 100644 repos/spack_repo/builtin/packages/csh/GNUmakefile create mode 100644 repos/spack_repo/builtin/packages/csh/linux-closefrom.patch create mode 100644 repos/spack_repo/builtin/packages/csh/macos-compat.patch create mode 100644 repos/spack_repo/builtin/packages/csh/no-publib.patch create mode 100644 repos/spack_repo/builtin/packages/csh/package.py diff --git a/repos/spack_repo/builtin/packages/csh/GNUmakefile b/repos/spack_repo/builtin/packages/csh/GNUmakefile new file mode 100644 index 00000000000..7a570765b9c --- /dev/null +++ b/repos/spack_repo/builtin/packages/csh/GNUmakefile @@ -0,0 +1,45 @@ +# GNU makefile for OpenBSD csh, whose BSD Makefile requires bsd.prog.mk. +# Generates const.h and error.h the same way the BSD Makefile does. +CFLAGS ?= -O2 +CFLAGS += -I. + +ifeq ($(shell uname -s),Linux) +CFLAGS += -D_GNU_SOURCE +LDLIBS += -lbsd +endif + +SRCS = alloc.c char.c const.c csh.c dir.c dol.c error.c exec.c exp.c file.c \ + func.c glob.c hist.c init.c lex.c misc.c parse.c proc.c sem.c set.c \ + str.c time.c +OBJS = $(SRCS:.c=.o) + +all: csh + +csh: $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS) + +$(OBJS): const.h error.h + +error.h: error.c + { echo '#ifndef _h_sh_err'; echo '#define _h_sh_err'; \ + grep -E '^#define[[:space:]]+ERR_' error.c; \ + echo '#endif /* _h_sh_err */'; } > $@ + +# csh.h includes const.h, so give the preprocessor an empty one to start +const.h: const.c error.h + : > $@ + $(CC) -E $(CPPFLAGS) $(CFLAGS) const.c | grep -E '^Char STR' | \ + sed -e 's/^Char \(STR[A-Za-z0-9_]*\).*/extern Char \1[];/' | \ + sort -u > $@.tmp + mv $@.tmp $@ + +PREFIX ?= /usr/local +install: csh + install -d $(DESTDIR)$(PREFIX)/bin $(DESTDIR)$(PREFIX)/share/man/man1 + install -m 755 csh $(DESTDIR)$(PREFIX)/bin + install -m 644 csh.1 $(DESTDIR)$(PREFIX)/share/man/man1 + +clean: + rm -f csh $(OBJS) const.h error.h + +.PHONY: all install clean diff --git a/repos/spack_repo/builtin/packages/csh/linux-closefrom.patch b/repos/spack_repo/builtin/packages/csh/linux-closefrom.patch new file mode 100644 index 00000000000..202478ddd25 --- /dev/null +++ b/repos/spack_repo/builtin/packages/csh/linux-closefrom.patch @@ -0,0 +1,10 @@ +--- a/misc.c ++++ b/misc.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + #include + + #include "csh.h" diff --git a/repos/spack_repo/builtin/packages/csh/macos-compat.patch b/repos/spack_repo/builtin/packages/csh/macos-compat.patch new file mode 100644 index 00000000000..76a82e36877 --- /dev/null +++ b/repos/spack_repo/builtin/packages/csh/macos-compat.patch @@ -0,0 +1,66 @@ +--- a/csh.h ++++ b/csh.h +@@ -38,7 +38,63 @@ + * BUFSIZ The i/o buffering size; also limits word size + * MAILINTVL How often to mailcheck; more often is more expensive + */ ++ ++/* ++ * macOS compatibility: shims for OpenBSD-isms missing from Darwin libc. ++ */ ++#ifdef __APPLE__ ++#include ++#include ++#include ++#include ++ ++#ifndef HOST_NAME_MAX ++#define HOST_NAME_MAX 255 ++#endif ++ ++/* Darwin has no reallocarray(3) */ ++static inline void * ++csh_reallocarray(void *p, size_t c, size_t n) ++{ ++ if (n != 0 && c > (size_t)-1 / n) ++ return (NULL); ++ return (realloc(p, c * n)); ++} ++#define reallocarray csh_reallocarray ++ ++/* Darwin strnvis(3) takes (dst, dstsize, src, flag); OpenBSD (dst, src, dstsize, flag) */ ++#define strnvis(dst, src, siz, flag) (strnvis)((dst), (siz), (src), (flag)) ++ ++/* Darwin has no closefrom(3) */ ++static inline void ++csh_closefrom(int fd) ++{ ++ int i, max = (int)sysconf(_SC_OPEN_MAX); + ++ if (max < 0) ++ max = 256; ++ for (i = fd; i < max; i++) ++ (void) close(i); ++} ++#define closefrom csh_closefrom ++ ++#ifndef timespecsub ++#define timespecsub(a, b, res) do { \ ++ (res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ ++ (res)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \ ++ if ((res)->tv_nsec < 0) { \ ++ (res)->tv_sec--; \ ++ (res)->tv_nsec += 1000000000L; \ ++ } \ ++} while (0) ++#endif ++#ifndef timespeccmp ++#define timespeccmp(a, b, CMP) \ ++ (((a)->tv_sec == (b)->tv_sec) ? \ ++ ((a)->tv_nsec CMP (b)->tv_nsec) : ((a)->tv_sec CMP (b)->tv_sec)) ++#endif ++#endif /* __APPLE__ */ ++ + #define FORKSLEEP 10 /* delay loop on non-interactive fork failure */ + #define MAILINTVL 600 /* 10 minutes */ + diff --git a/repos/spack_repo/builtin/packages/csh/no-publib.patch b/repos/spack_repo/builtin/packages/csh/no-publib.patch new file mode 100644 index 00000000000..6e309bb4e0a --- /dev/null +++ b/repos/spack_repo/builtin/packages/csh/no-publib.patch @@ -0,0 +1,28 @@ +--- a/glob.c ++++ b/glob.c +@@ -39,7 +39,6 @@ + #include + #include + #include +-#include + + #include "csh.h" + #include "extern.h" +@@ -435,7 +434,7 @@ + if (append) { + if (nvp + append >= nv + size) { + size = (nvp - nv) + append + GLOBSPACE; +- nv = xrealloc((void*) nv, sizeof(Char *) * size); ++ nv = xreallocarray(nv, size, sizeof(Char *)); + nvp = nv + size - append - GLOBSPACE; + } + if (*magicp && globv.gl_pathc) { +@@ -456,7 +455,7 @@ + * Output is empty, or none of the magic patterns matched. + * It's up to the caller to generate a "No match" error. + */ +- xfree(nv); ++ free(nv); + return (NULL); + } + *nvp = NULL; diff --git a/repos/spack_repo/builtin/packages/csh/package.py b/repos/spack_repo/builtin/packages/csh/package.py new file mode 100644 index 00000000000..3f017097402 --- /dev/null +++ b/repos/spack_repo/builtin/packages/csh/package.py @@ -0,0 +1,113 @@ +# Copyright Spack Project Developers. See COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from spack_repo.builtin.build_systems.makefile import MakefilePackage + +from spack.package import * + + +def debian_patch(name, sha256, **kwargs): + """Patch from the debian/patches series of Debian's csh source package.""" + url = f"https://sources.debian.org/data/main/c/csh/20240808-4/debian/patches/{name}" + patch(url, sha256=sha256, **kwargs) + + +class Csh(MakefilePackage): + """The original BSD C shell: OpenBSD's csh(1), via Debian's csh source package. + + This is plain csh, without tcsh extensions. + """ + + homepage = "https://tracker.debian.org/pkg/csh" + url = "https://deb.debian.org/debian/pool/main/c/csh/csh_20240808.orig.tar.xz" + + maintainers("tgamblin") + + license("BSD-3-Clause") + + version("20240808", sha256="df916baa73c264516177c6667cc0a061f6eb9743f862b625f17067d74a3f4d1c") + + depends_on("c", type="build") + depends_on("libbsd", when="platform=linux") + + # Debian's patches, applied in series order. glibc portability patches are + # linux-only; the arithmetic fix and pledge(2) removal (an OpenBSD-only + # syscall) apply everywhere. + with when("platform=linux"): + debian_patch( + "02_libbsd.diff", + sha256="bdb992adcbf17794b7d5cd2912c4d92c22b612d6234e7e27ddc2715e4e92ebff", + ) + debian_patch( + "03_maxpathlen.diff", + sha256="418cd4c77faf52f62765287e5513eaef97e475d9cfb6d434953bf2001b4b61b2", + ) + debian_patch( + "04_fpurge.diff", + sha256="a3acaa94c88eb02878f98fa034780380732fec3b278552ed40a208cfc21bea24", + ) + debian_patch( + "06_time_h_for_time_t.diff", + sha256="8eb5fc09dee920fd3042d87ab51ba3946699e36dfb00b8a257ca01730b92907c", + ) + debian_patch( + "07_funopen.diff", + sha256="28d5e49a1470ea9b4160ae3695207abfb00c6b1f7446c467bc34b35403664a0b", + ) + debian_patch( + "08_glob.diff", + sha256="db0e90b343560a52cd27425b8757796b137887fe51b12e7b1f428ae7cc5cc3cd", + ) + debian_patch( + "09_sys_signame.diff", + sha256="f8b105923f58bf29b986da51cc9278491a3545fdeb30bd8c4ed88e1a4605ccfd", + ) + debian_patch( + "13_fix_arithmetic_precedence.diff", + sha256="20343669212e4ee64aeb56b61a4d064d5640e0f62fd78df1f3b57d7943196dd5", + ) + with when("platform=linux"): + debian_patch( + "15_glibc-strsignal.diff", + sha256="1fb4aa794b15197d4a8e731974ac2c244e6de21b16487d51b3179972b5e77f9b", + ) + debian_patch( + "16_missing_tiocstat.diff", + sha256="03357ae8ccac6274550222e43223d01f18dc9a05e56ad0ba0846663a3eb0ec7f", + ) + debian_patch( + "17_no_pledge.diff", + sha256="0ef230820c3a10ee03b6e5a45be281ed818bb3541668273baecf09152512ddeb", + ) + with when("platform=linux"): + debian_patch( + "18_g++-14.diff", + sha256="bbdc19b94f8ffad7600e7386018b3321bc4ebd70c0bd327ddc3512eeb86ee538", + ) + + # use in-tree xmalloc/xreallocarray in Debian's glob code instead of publib + patch("no-publib.patch", when="platform=linux") + + # closefrom(3) needs libbsd's declaration before glibc 2.34 + patch("linux-closefrom.patch", when="platform=linux") + + # shims for OpenBSD-isms missing from Darwin libc + patch("macos-compat.patch", when="platform=darwin") + + sanity_check_is_file = [join_path("bin", "csh")] + + def edit(self, spec, prefix): + # upstream's BSD Makefile requires bsd.prog.mk; GNU make prefers + # GNUmakefile, so ours takes over without touching the original + copy(join_path(self.package_dir, "GNUmakefile"), "GNUmakefile") + + @property + def install_targets(self): + return ["install", f"PREFIX={self.prefix}"] + + def test_run(self): + """run a simple csh script""" + csh = Executable(self.prefix.bin.csh) + out = csh("-f", "-c", "set x = (a b c); echo $x[2-]:q", output=str) + assert "b c" in out