This commit is contained in:
2025-07-23 13:40:29 -04:00
parent f62cecbfd3
commit 9e94f5ce44
7 changed files with 255 additions and 202 deletions

BIN
c-out/bin/yait Executable file

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,4 @@
PREFIX=/usr/bin/ PREFIX=/usr/bin/
CFLAGS=-Wall -Wextra -O2 CFLAGS=-Wall -Wextra -g
LDFLAGS= LDFLAGS=
CC=gcc CC=clang

View File

@@ -4,32 +4,9 @@
make dist-clean make dist-clean
lint_file() {
local output
output=$(clang-tidy "$1" 2>&1 | grep -v -E 'Error while trying to load a compilation database|No compilation database found|fixed-compilation-database:|json-compilation-database:|Running without flags.')
if [[ -n "$output" ]]; then
echo "[LINT] $1:"
echo "$output"
fi
}
whitespace_cleanup() {
sed -i 's/[ \t]*$//' "$1"
awk 'BEGIN{ORS=""} {print $0 "\n"} END{}' "$1" > "$1.tmp" && mv "$1.tmp" "$1"
}
comment_check() {
if grep -n -E 'TODO|FIXME' "$1"; then
echo "[WARN] $1 contains TODO/FIXME comments."
fi
}
process_file() { process_file() {
clang-format -i "$1" clang-format -i "$1"
tools/check_header_footer "$1" tools/check_header_footer "$1"
lint_file "$1"
whitespace_cleanup "$1"
comment_check "$1"
} }
if [[ $# -gt 0 ]]; then if [[ $# -gt 0 ]]; then

44
tools/check_header Executable file
View File

@@ -0,0 +1,44 @@
#!/bin/env bash
# Usage: ./check_header
if pwd | grep -q tools; then
cd ..
fi
files=$(find yait \( -name '*.c' -o -name '*.h' \))
files+=" "
files+=$(find core \( -name '*.c' -o -name '*.h' \))
files+=" $(find . -maxdepth 1 -type f)"
ignore="README COPYING .clang-format config.mak"
if [ -z "$files" ]; then
echo "No files found"
exit 0
fi
missing=""
for file in $files; do
if echo "$ignore" | grep -qw "$(basename "$file")"; then
continue
fi
echo -ne "$file... "
if grep -q "Copyright (C)" "$file"; then
echo -e "\033[1;32mOK\033[0m"
else
echo -e "\033[0;31mFAIL\033[0m"
missing+="$file "
fi
done
if [ "$missing" = "" ]; then
echo -e "\033[1;32mAll checks pass.\033[0m"
else
echo -e "\033[0;31mThe follwing files are missing copyright information.\033[0m"
fi
for file in $missing; do
echo " - $file"
done

View File

@@ -1,71 +0,0 @@
#!/bin/bash
# Usage: ./ensure_header_footer.sh filename
FILE="$1"
HEADER=$(cat <<'EOF'
// Copyright (C) 2025 vx_clutch ( owestness@gmail.com )
// See end of file for extended copyright information.
EOF
)
HEADER+=$'\n'
FOOTER=$(cat <<'EOF'
/* yait is yet another init tool.
* Copyright (C) 2025 vx-clutch
*
* This file is part of yait.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions, and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions, and the following disclaimer in the documentation or
* other materials provided with the distribution.
* 3. Neither the name of vx-clutch nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
EOF
)
if [[ -z "$FILE" ]]; then
echo "Usage: $0 <filename>"
exit 1
fi
if [[ ! -f "$FILE" ]]; then
echo "Error: File '$FILE' does not exist."
exit 1
fi
TMP_FILE="$(mktemp)"
read -r FIRST_LINE < "$FILE"
if [[ "$FIRST_LINE" != "$HEADER" ]]; then
echo "$HEADER" > "$TMP_FILE"
cat "$FILE" >> "$TMP_FILE"
mv "$TMP_FILE" "$FILE"
else
rm "$TMP_FILE"
fi
if [[ "$(tail -n 1 "$FILE")" != "$FOOTER" ]]; then
echo "$FOOTER" >> "$FILE"
fi
echo "Checked '$FILE' for header/footer."

View File

@@ -1,9 +1,12 @@
// Usage: yait [OPTION]... [PROJECT] (NAME)
#include "../config.h" #include "../config.h"
#include "../core/file.h" #include "../core/file.h"
#include "../core/print.h" #include "../core/print.h"
#include "../core/standard.h" #include "../core/standard.h"
#include "contents.h" #include "contents.h"
#include "format.h" #include "format.h"
#include <errno.h>
#include <pwd.h> #include <pwd.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -21,14 +24,39 @@
#define print_option(option, description) \ #define print_option(option, description) \
printf (" %-20s %-20s\n", option, description) printf (" %-20s %-20s\n", option, description)
#define on_error(msg, code) \
if (err) \
{ \
printfn (msg ": %s", strerror (err)); \
return code; \
}
int create_license_if_needed(format_t); #ifdef DEBUG
#define debug(fmt, ...) \
fprintf (stderr, "\e[0;32m[%8d]\e[0;33m " fmt "\e[0m\n", __LINE__, \
##__VA_ARGS__)
#define debugc(fmt, ...) \
fprintf (stderr, "\e[0;32m[%8d]\e[0;33m " fmt "...\e[0m", __LINE__, \
##__VA_ARGS__)
#define done fprintf(stderr, "done.\n")
#else
#define debug(fmt, ...)
#define debugc(fmt, ...)
#define done
#endif
// int create_license_if_needed (format_t);
// int get_license_line_and_create_license (format_t, char **);
int create_configure ();
int create_makefile (format_t); int create_makefile (format_t);
int create_project (format_t); int create_project (format_t);
int get_license_line_and_create_license(format_t, char**); int generate_source_files (format_t);
int maybe_apply_clang_format (format_t); int maybe_apply_clang_format (format_t);
int reset_path_ ();
int sanitize (format_t *); int sanitize (format_t *);
int setup_git_and_permissions(format_t); int setup_git (format_t);
int depth;
void void
usage (int status) usage (int status)
@@ -42,8 +70,10 @@ usage(int status)
printf ("Usage: yait [OPTION]... [PROJECT] (NAME)\n"); printf ("Usage: yait [OPTION]... [PROJECT] (NAME)\n");
printf ("Creates a C project with opinionated defaults.\n"); printf ("Creates a C project with opinionated defaults.\n");
printf ("When only given the first argument it will detect your name.\n\n"); printf ("When only given the first argument it will detect your name.\n\n");
printf("Mandatory arguments to long options are mandatory for short options too\n"); printf ("Mandatory arguments to long options are mandatory for short "
print_option("-l, --license=NAME", "Set license (gpl, mit, bsd) [default: gpl]"); "options too\n");
print_option ("-l, --license=NAME",
"Set license (gpl, mit, bsd) [default: bsd]");
print_option ("--use-cpp", "Uses the CPP language instead of C"); print_option ("--use-cpp", "Uses the CPP language instead of C");
print_option ("--git", "Initialize git repository"); print_option ("--git", "Initialize git repository");
print_option ("--GNU", "Adds standard GNU argument parsing to your project"); print_option ("--GNU", "Adds standard GNU argument parsing to your project");
@@ -69,9 +99,10 @@ main(int argc, char **argv)
return status; return status;
} }
format_t conf; format_t conf = { 0 };
conf.project = argv[0];
conf.name = (argc > 2) ? argv[1] : NULL; conf.project = argv[0]; // fix: project name is argv[1]
conf.name = (argc > 1) ? argv[1] : NULL; // fix: name is optional, at argv[2]
if (!conf.name) if (!conf.name)
{ {
@@ -82,9 +113,7 @@ main(int argc, char **argv)
conf.flag.git = DEFAULT_GIT_INIT; conf.flag.git = DEFAULT_GIT_INIT;
conf.flag.clang_format = DEFAULT_CLANG_FORMAT; conf.flag.clang_format = DEFAULT_CLANG_FORMAT;
conf.licence = DEFAULT_LICENSE; conf.licence = DEFAULT_LICENSE;
#ifdef DEBUG
system(strcat("rm -rf ", conf.project));
#endif
return create_project (conf); return create_project (conf);
} }
@@ -93,47 +122,50 @@ create_project(format_t fmt)
{ {
int err; int err;
err = create_and_enter_directory(fmt.project); debugc ("sanitize... ");
if (err)
{
printfn("failed to create or enter directory: %s", strerror(err));
return err;
}
err = sanitize (&fmt); err = sanitize (&fmt);
if (err) on_error ("failed to sanitize format", err);
{ done;
printfn("failed to sanitize format: %s", strerror(err));
return err;
}
err = create_license_if_needed(fmt); debugc ("take %s", fmt.project);
if (err) err = create_and_enter_directory (fmt.project);
{ on_error ("failed to create or enter directory", err);
printfn("failed to create license: %s", strerror(err)); done;
return err; depth = 0;
}
// debug ("create licenseing");
// err = create_license_if_needed (fmt);
// on_error ("failed to create license", err);
debugc ("create makefile");
err = create_makefile (fmt); err = create_makefile (fmt);
if (err) on_error ("failed to create Makefile", err);
{ done;
printfn("failed to create Makefile: %s", strerror(err));
return err;
}
err = setup_git_and_permissions(fmt); debug ("setup git");
err = setup_git (fmt);
if (err) if (err)
{
printfn ("warning: git initialization failed: %s", strerror (err)); printfn ("warning: git initialization failed: %s", strerror (err));
// continue even if git fails
}
debug ("create .clang-format");
err = maybe_apply_clang_format (fmt); err = maybe_apply_clang_format (fmt);
if (err) if (err)
{
printfn ("warning: clang-format setup failed: %s", strerror (err)); printfn ("warning: clang-format setup failed: %s", strerror (err));
return 0;
} }
#define reset_path reset_path_ ()
int
reset_path_ ()
{
while (depth != 0)
{
if (chdir ("..") != 0)
return errno;
else
depth--;
}
return 0; return 0;
} }
@@ -142,33 +174,38 @@ sanitize(format_t *fmt)
{ {
if (!fmt->name) if (!fmt->name)
fmt->name = DEFAULT_USER_NAME; fmt->name = DEFAULT_USER_NAME;
return 0; return 0;
} }
int // int
create_license_if_needed(format_t fmt) // create_license_if_needed (format_t fmt)
{ // {
char *license_line = NULL; // char *license_line = NULL;
return get_license_line_and_create_license(fmt, &license_line); // return get_license_line_and_create_license (fmt, &license_line);
} // }
//
int // int
get_license_line_and_create_license(format_t fmt, char **license_line_buffer) // get_license_line_and_create_license (format_t fmt, char
{ // **license_line_buffer)
switch (fmt.licence) // {
{ // switch (fmt.licence)
case BSD3: // {
*license_line_buffer = // case BSD3:
"License BSD-3-Clause: BSD-3-Clause <https://opensource.org/licence/bsd-3-clause>"; // *license_line_buffer = "License BSD-3-Clause: BSD-3-Clause "
return create_file_with_content("COPYING", bsd3_license_template, YEAR, fmt.name); // "<https://opensource.org/license/bsd-3-clause/>";
// return create_file_with_content ("COPYING", bsd3_license_template,
case GPLv3: // YEAR,
default: // fmt.name);
*license_line_buffer = "License GPLv3: GNU GPL version 3 <https://www.gnu.org/licenses/gpl-3.0.html>"; //
return create_file_with_content("COPYING", gplv3_license_template, YEAR, fmt.name); // case GPLv3:
} // default:
} // *license_line_buffer = "License GPLv3: GNU GPL version 3 "
// "<https://www.gnu.org/licenses/gpl-3.0.html>";
// return create_file_with_content ("COPYING", gplv3_license_template,
// YEAR,
// fmt.name);
// }
// }
int int
maybe_apply_clang_format (format_t fmt) maybe_apply_clang_format (format_t fmt)
@@ -176,20 +213,86 @@ maybe_apply_clang_format(format_t fmt)
if (!fmt.flag.clang_format) if (!fmt.flag.clang_format)
return 0; return 0;
const char *clang_fmt = "BasedOnStyle: LLVM\nIndentWidth: 2\nUseTab: Never\n"; reset_path;
char *clang_fmt = "BasedOnStyle: LLVM\nIndentWidth: 2\nUseTab: Never\n";
return create_file_with_content (".clang-format", clang_fmt, 0, NULL); return create_file_with_content (".clang-format", clang_fmt, 0, NULL);
} }
int int
setup_git_and_permissions(format_t fmt) setup_git (format_t fmt)
{ {
if (!fmt.flag.git) if (!fmt.flag.git)
return 0; return 0;
reset_path;
int err = system ("git init --quiet"); int err = system ("git init --quiet");
if (err) if (err)
{
printfn ("failed on git initialize: %s", strerror (err)); printfn ("failed on git initialize: %s", strerror (err));
}
return err; return err;
} }
int
create_makefile (format_t fmt)
{
char *makefile_name = strdup (fmt.project);
if (!makefile_name)
{
printfn ("fatal: out of memory");
return 1;
}
for (char *p = makefile_name; *p; ++p)
if (*p >= 'a' && *p <= 'z')
*p -= 32;
reset_path;
create_file_with_content ("Makefile", makefile_template, makefile_name,
makefile_name, makefile_name, makefile_name,
makefile_name, makefile_name, fmt.project,
makefile_name);
free (makefile_name);
return 0;
}
int
create_configure ()
{
reset_path;
create_file_with_content ("configure", configure_template);
int err = system ("chmod +x configure");
if (err)
printfn ("error: %s", strerror (err));
return err;
}
int
generate_source_files (format_t fmt)
{
int err;
debug ("take %s/%s", fmt.project, fmt.project);
err = create_and_enter_directory (fmt.project);
on_error ("failed to create or enter directory", err);
if (fmt.flag.GNU)
{
debug ("GNU flag source branch");
create_file_with_content ("main.c", main_c_gnu_template);
goto atexit_clean;
}
debug ("default sourcebranch");
create_file_with_content ("main.c", main_c_template);
atexit_clean:
reset_path;
return 0;
}