diff options
| -rwxr-xr-x | .github/workflows/archlinux.sh | 41 | ||||
| -rw-r--r-- | .github/workflows/build.yml | 163 | ||||
| -rwxr-xr-x | .github/workflows/builds.sh | 64 | ||||
| -rw-r--r-- | .github/workflows/builds.yml | 75 | ||||
| -rwxr-xr-x | .github/workflows/debian.sh | 42 | ||||
| -rwxr-xr-x | .github/workflows/fedora.sh | 42 | ||||
| -rw-r--r-- | .github/workflows/notify.yml | 44 | ||||
| -rw-r--r-- | .github/workflows/pages.yml | 139 | ||||
| -rw-r--r-- | .github/workflows/quality.yml | 134 | ||||
| -rw-r--r-- | .github/workflows/release.yml | 120 | ||||
| -rwxr-xr-x | .github/workflows/ubuntu.sh | 43 | ||||
| -rw-r--r-- | configure.ac | 1 | ||||
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/lexer.c | 36 | ||||
| -rw-r--r-- | src/mate-calc-application.c | 227 | ||||
| -rw-r--r-- | src/mate-calc-application.h | 44 | ||||
| -rw-r--r-- | src/mate-calc-cmd.c | 1 | ||||
| -rw-r--r-- | src/mate-calc.c | 245 | ||||
| -rw-r--r-- | src/math-equation.c | 26 | ||||
| -rw-r--r-- | src/math-window.c | 155 | ||||
| -rw-r--r-- | src/math-window.h | 9 | ||||
| -rw-r--r-- | src/meson.build | 1 | ||||
| -rw-r--r-- | src/mp-equation.c | 5 | ||||
| -rw-r--r-- | src/parser.c | 6 | ||||
| -rw-r--r-- | src/parserfunc.c | 54 |
25 files changed, 780 insertions, 939 deletions
diff --git a/.github/workflows/archlinux.sh b/.github/workflows/archlinux.sh new file mode 100755 index 0000000..e7ba154 --- /dev/null +++ b/.github/workflows/archlinux.sh @@ -0,0 +1,41 @@ +#!/usr/bin/bash + +# Use grouped output messages +infobegin() { + echo "::group::${1}" +} +infoend() { + echo "::endgroup::" +} + +# Required packages on Archlinux +requires=( + ccache # Use ccache to speed up build + clang # Build with clang on Archlinux + meson # Used for meson build +) + +# https://gitlab.archlinux.org/archlinux/packaging/packages/mate-calc +requires+=( + autoconf-archive + gcc + gettext + git + glib2-devel + gtk3 + intltool + itstool + libmpc + make + mate-common + which + yelp-tools +) + +infobegin "Update system" +pacman --noconfirm -Syu +infoend + +infobegin "Install dependency packages" +pacman --noconfirm -S ${requires[@]} +infoend diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index dc7143a..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,163 +0,0 @@ -name: Build and Test - -on: - push: - branches: [ master, main ] - tags: [ 'v*' ] - pull_request: - branches: [ master, main ] - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - distro: - - 'debian:testing' - - 'fedora:latest' - include: - - distro: 'debian:testing' - distro_name: 'debian' - - distro: 'fedora:latest' - distro_name: 'fedora' - - container: - image: ${{ matrix.distro }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install dependencies (Debian) - if: matrix.distro_name == 'debian' - run: | - apt-get update - apt-get install -y \ - autopoint \ - clang \ - clang-tools \ - cppcheck \ - gcc \ - git \ - libatk1.0-dev \ - libglib2.0-dev \ - libgmp-dev \ - libgtk-3-dev \ - libmpc-dev \ - libmpfr-dev \ - libxml2-dev \ - make \ - mate-common \ - yelp-tools \ - bison \ - flex \ - curl - - - name: Install dependencies (Fedora) - if: matrix.distro_name == 'fedora' - run: | - dnf update -y - dnf install -y \ - autoconf-archive \ - clang \ - clang-analyzer \ - cppcheck-htmlreport \ - bison \ - desktop-file-utils \ - flex \ - gcc \ - git \ - gmp-devel \ - gtk3 \ - libmpc-devel \ - libxml2-devel \ - make \ - mate-common \ - mate-desktop-devel \ - mpfr-devel \ - redhat-rpm-config \ - curl \ - which - - - name: Set up environment variables - run: | - export CPU_COUNT=$(nproc) - echo "CPU_COUNT=$CPU_COUNT" >> $GITHUB_ENV - echo "DISTRO_NAME=${{ matrix.distro_name }}" >> $GITHUB_ENV - echo "REPO_NAME=mate-calc" >> $GITHUB_ENV - echo "OWNER_NAME=mate-desktop" >> $GITHUB_ENV - export CHECKERS="-enable-checker deadcode.DeadStores -enable-checker alpha.deadcode.UnreachableCode -enable-checker alpha.core.CastSize -enable-checker alpha.core.CastToStruct -enable-checker alpha.core.IdenticalExpr -enable-checker alpha.core.SizeofPtr -enable-checker alpha.security.ArrayBoundV2 -enable-checker alpha.security.MallocOverflow -enable-checker alpha.security.ReturnPtrRange -enable-checker alpha.unix.SimpleStream -enable-checker alpha.unix.cstring.BufferOverlap -enable-checker alpha.unix.cstring.NotNullTerminated -enable-checker alpha.unix.cstring.OutOfBounds -enable-checker alpha.core.FixedAddr -enable-checker security.insecureAPI.strcpy" - echo "CHECKERS=$CHECKERS" >> $GITHUB_ENV - - - name: Run cppcheck (Debian only) - if: matrix.distro_name == 'debian' - shell: bash - run: | - export CFLAGS="${CFLAGS:-} -Wsign-compare" - cppcheck --enable=warning,style,performance,portability,information,missingInclude . - - - name: Generate build system - run: | - NOCONFIGURE=1 ./autogen.sh - - - name: Configure with scan-build - run: | - scan-build $CHECKERS ./configure --enable-compile-warnings=maximum - - - name: Build with scan-build - shell: bash - run: | - if [ $CPU_COUNT -gt 1 ]; then - if [ "$DISTRO_NAME" == "debian" ]; then - scan-build $CHECKERS --keep-cc --use-cc=clang --use-c++=clang++ -o html-report make -j $CPU_COUNT - make clean - fi - scan-build $CHECKERS --keep-cc -o html-report make -j $CPU_COUNT - else - if [ "$DISTRO_NAME" == "debian" ]; then - scan-build $CHECKERS --keep-cc --use-cc=clang --use-c++=clang++ -o html-report make - make clean - fi - scan-build $CHECKERS --keep-cc -o html-report make - fi - - - name: Run additional checks (Fedora only) - if: matrix.distro_name == 'fedora' - run: | - cppcheck --xml --output-file=cppcheck.xml --enable=warning,style,performance,portability,information,missingInclude . - cppcheck-htmlreport --title=$REPO_NAME --file=cppcheck.xml --report-dir=cppcheck-htmlreport - - - name: Generate index (Fedora only) - if: matrix.distro_name == 'fedora' - run: | - curl -Ls -o gen-index https://github.com/mate-desktop/mate-dev-scripts/raw/master/travis/gen-index.sh - chmod +x gen-index - ./gen-index -l 20 -i https://github.com/${OWNER_NAME}/mate-icon-theme/raw/master/mate/16x16/apps/accessories-calculator.png - - - name: Run distcheck - run: | - make distcheck - - - name: Upload HTML reports (Fedora only) - if: matrix.distro_name == 'fedora' - uses: actions/upload-artifact@v4 - with: - name: html-report-${{ matrix.distro_name }} - path: html-report/ - retention-days: 30 - - - name: Upload cppcheck reports (Fedora only) - if: matrix.distro_name == 'fedora' - uses: actions/upload-artifact@v4 - with: - name: cppcheck-report-${{ matrix.distro_name }} - path: cppcheck-htmlreport/ - retention-days: 30 - - - name: Upload distribution archives - if: matrix.distro_name == 'fedora' - uses: actions/upload-artifact@v4 - with: - name: distribution-archives - path: mate-calc-*.tar.xz - retention-days: 90
\ No newline at end of file diff --git a/.github/workflows/builds.sh b/.github/workflows/builds.sh new file mode 100755 index 0000000..20c1ad5 --- /dev/null +++ b/.github/workflows/builds.sh @@ -0,0 +1,64 @@ +#!/usr/bin/bash + +set -e +set -o pipefail + +CPUS=$(grep processor /proc/cpuinfo | wc -l) + +# Use grouped output messages +infobegin() { + echo "::group::${1}" +} +infoend() { + echo "::endgroup::" +} + +# Run meson first, then run autotools +# Because meson dist requires a clean git workspace +# Autotools will modify some files (such as po, etc.), making them dirty. +if [ -f meson.build ]; then + + infobegin "Configure (meson)" + meson setup _build --prefix=/usr + infoend + + infobegin "Build (meson)" + meson compile -C _build + infoend + + infobegin "Test (meson)" + ninja -C _build test + infoend + + infobegin "Dist (meson)" + # Git safedirectory stop ninja dist + # https://github.com/git/git/commit/8959555cee7ec045958f9b6dd62e541affb7e7d9 + # https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory + git config --global --add safe.directory ${PWD} + ninja -C _build dist + infoend +fi + +if [ -f autogen.sh ]; then + infobegin "Configure (autotools)" + NOCONFIGURE=1 ./autogen.sh + ./configure --prefix=/usr --enable-compile-warnings=maximum || { + cat config.log + exit 1 + } + infoend + + infobegin "Build (autotools)" + make -j ${CPUS} + infoend + + infobegin "Check (autotools)" + make -j ${CPUS} check || { + true + } + infoend + + infobegin "Distcheck (autotools)" + make -j ${CPUS} distcheck + infoend +fi diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml new file mode 100644 index 0000000..b7d7864 --- /dev/null +++ b/.github/workflows/builds.yml @@ -0,0 +1,75 @@ +name: CI Build + +on: + push: + branches: + - master + pull_request: + branches: + - master + workflow_dispatch: + +# cancel already running builds of the same branch or pull request +concurrency: + group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.head_ref || github.sha }} + cancel-in-progress: true + + +jobs: + build: + name: Build on ${{matrix.container}} (using ${{matrix.cc}}) + runs-on: ubuntu-latest + container: + image: ${{matrix.container}} + + strategy: + fail-fast: false # don't cancel other jobs in the matrix if one fails + matrix: + container: + [ + "debian:testing", + "fedora:latest", + "ubuntu:rolling", + "archlinux:latest", + ] + cc: ["gcc"] + cxx: ["g++"] + include: + - container: "archlinux:latest" + cc: "clang" + cxx: "clang++" + + env: + # Speed up build with ccache + CC: ccache ${{ matrix.cc }} + CXX: ccache ${{ matrix.cxx }} + CONTAINER: ${{ matrix.container }} + + steps: + - name: Setup environment variables + id: distro-name + shell: bash + run: | + split=(${CONTAINER//:/ }) + distro=${split[0]} + short_sha=${SHA:0:8} + echo "DISTRO=$distro" | tee -a $GITHUB_ENV + - name: Install git command + shell: bash + run: | + echo "::group::Install git ..." + apt-get update -qq && apt-get install --assume-yes git || true + dnf update -y && dnf install -y git || true + pacman --noconfirm -Sy git || true + echo "::endgroup::" + - name: Repository checkout + uses: actions/checkout@v5 + - name: Install dependency packages + run: .github/workflows/${{ env.DISTRO }}.sh + - name: Enable ccache to speed up builds + uses: hendrikmuhs/[email protected] + with: + key: ${{ env.DISTRO }}-${{ matrix.cc }} + + - name: Build the source code + run: .github/workflows/builds.sh diff --git a/.github/workflows/debian.sh b/.github/workflows/debian.sh new file mode 100755 index 0000000..9f5b1bd --- /dev/null +++ b/.github/workflows/debian.sh @@ -0,0 +1,42 @@ +#!/usr/bin/bash + +# Use grouped output messages +infobegin() { + echo "::group::${1}" +} +infoend() { + echo "::endgroup::" +} + +# Required packages on Debian +requires=( + ccache # Use ccache to speed up build + meson # Used for meson build +) + +# https://salsa.debian.org/debian-mate-team/mate-calc +requires+=( + autopoint + bison + flex + libatk1.0-dev + libglib2.0-dev + libgmp-dev + libgtk-3-dev + libmpc-dev + libmpfr-dev + libxml2-dev + make + mate-common + yelp-tools +) + +infobegin "Update system" +apt-get update -qq +infoend + +infobegin "Install dependency packages" +env DEBIAN_FRONTEND=noninteractive \ + apt-get install --assume-yes \ + ${requires[@]} +infoend diff --git a/.github/workflows/fedora.sh b/.github/workflows/fedora.sh new file mode 100755 index 0000000..7dd4d6b --- /dev/null +++ b/.github/workflows/fedora.sh @@ -0,0 +1,42 @@ +#!/usr/bin/bash + +# Use grouped output messages +infobegin() { + echo "::group::${1}" +} +infoend() { + echo "::endgroup::" +} + +# Required packages on Fedora +requires=( + ccache # Use ccache to speed up build + meson # Used for meson build +) + +# https://src.fedoraproject.org/cgit/rpms/mate-calc.git +requires+=( + autoconf-archive + bison + desktop-file-utils + flex + gcc + git + gmp-devel + gtk3-devel + libmpc-devel + libxml2-devel + make + mate-common + mate-desktop-devel + mpfr-devel + redhat-rpm-config +) + +infobegin "Update system" +dnf update -y +infoend + +infobegin "Install dependency packages" +dnf install -y ${requires[@]} +infoend diff --git a/.github/workflows/notify.yml b/.github/workflows/notify.yml deleted file mode 100644 index 12e39ee..0000000 --- a/.github/workflows/notify.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Notifications - -on: - push: - branches: [ master, main ] - tags: [ 'v*' ] - workflow_run: - workflows: ["Build and Test"] - types: - - completed - -jobs: - irc-notify: - runs-on: ubuntu-latest - if: > - (github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/'))) || - (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'failure') - - steps: - - name: Send IRC notification on success - if: > - (github.event_name == 'push') || - (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') - uses: Gottox/irc-message-action@v2 - continue-on-error: true - with: - server: irc.libera.chat - channel: '#mate-dev' - nickname: mate-github-bot - message: | - [mate-calc] ${{ github.actor }}: ${{ github.event.head_commit.message || 'Workflow completed successfully' }} - [${{ github.ref_name }}] ${{ github.sha }} Success ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - - - name: Send IRC notification on failure - if: github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'failure' - uses: Gottox/irc-message-action@v2 - continue-on-error: true - with: - server: irc.libera.chat - channel: '#mate-dev' - nickname: mate-github-bot - message: | - [mate-calc] Build failed: ${{ github.event.workflow_run.head_commit.message }} - [${{ github.event.workflow_run.head_branch }}] ${{ github.event.workflow_run.head_sha }} Failure ${{ github.event.workflow_run.html_url }}
\ No newline at end of file diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml deleted file mode 100644 index c1c1af6..0000000 --- a/.github/workflows/pages.yml +++ /dev/null @@ -1,139 +0,0 @@ -name: Deploy to GitHub Pages - -on: - push: - branches: [ master, main ] - pull_request: - branches: [ master, main ] - -permissions: - contents: read - pages: write - id-token: write - pull-requests: write - -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - build-docs: - runs-on: ubuntu-latest - container: - image: 'fedora:latest' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - dnf update -y - dnf install -y \ - autoconf-archive \ - clang \ - clang-analyzer \ - cppcheck-htmlreport \ - bison \ - desktop-file-utils \ - flex \ - gcc \ - git \ - gmp-devel \ - gtk3 \ - libmpc-devel \ - libxml2-devel \ - make \ - mate-common \ - mate-desktop-devel \ - mpfr-devel \ - redhat-rpm-config \ - curl \ - which - - - name: Set up environment variables - run: | - export CPU_COUNT=$(nproc) - echo "CPU_COUNT=$CPU_COUNT" >> $GITHUB_ENV - echo "REPO_NAME=mate-calc" >> $GITHUB_ENV - echo "OWNER_NAME=mate-desktop" >> $GITHUB_ENV - export CHECKERS="-enable-checker deadcode.DeadStores -enable-checker alpha.deadcode.UnreachableCode -enable-checker alpha.core.CastSize -enable-checker alpha.core.CastToStruct -enable-checker alpha.core.IdenticalExpr -enable-checker alpha.core.SizeofPtr -enable-checker alpha.security.ArrayBoundV2 -enable-checker alpha.security.MallocOverflow -enable-checker alpha.security.ReturnPtrRange -enable-checker alpha.unix.SimpleStream -enable-checker alpha.unix.cstring.BufferOverlap -enable-checker alpha.unix.cstring.NotNullTerminated -enable-checker alpha.unix.cstring.OutOfBounds -enable-checker alpha.core.FixedAddr -enable-checker security.insecureAPI.strcpy" - echo "CHECKERS=$CHECKERS" >> $GITHUB_ENV - - - name: Generate build system - run: | - NOCONFIGURE=1 ./autogen.sh - - - name: Configure with scan-build - run: | - scan-build $CHECKERS ./configure --enable-compile-warnings=maximum - - - name: Build with scan-build - run: | - scan-build $CHECKERS --keep-cc -o html-report make -j $CPU_COUNT - - - name: Run cppcheck - run: | - cppcheck --xml --output-file=cppcheck.xml --enable=warning,style,performance,portability,information,missingInclude . - cppcheck-htmlreport --title=$REPO_NAME --file=cppcheck.xml --report-dir=cppcheck-htmlreport - - - name: Generate index page - run: | - curl -Ls -o gen-index https://github.com/mate-desktop/mate-dev-scripts/raw/master/travis/gen-index.sh - chmod +x gen-index - ./gen-index -l 20 -i https://github.com/${OWNER_NAME}/mate-icon-theme/raw/master/mate/16x16/apps/accessories-calculator.png - - - name: Setup Pages - if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' - uses: actions/configure-pages@v5 - - - name: Upload artifact - if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' - uses: actions/upload-pages-artifact@v3 - with: - path: html-report - - - name: Comment on PR with analysis results - if: github.event_name == 'pull_request' - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs'); - const path = require('path'); - - // Check if html-report exists and has content - const reportDir = 'html-report'; - let reportCount = 0; - - if (fs.existsSync(reportDir)) { - const files = fs.readdirSync(reportDir); - reportCount = files.filter(f => f.endsWith('.html')).length; - } - - const comment = `## 🔍 Static Analysis Results - - Code analysis completed for this pull request. - - - **Scan-build reports**: ${reportCount} files generated - - **Status**: ${reportCount > 0 ? '⚠️ Issues found' : '✅ No issues found'} - - The detailed reports are available in the workflow artifacts.`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: comment - }); - - deploy: - if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build-docs - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4
\ No newline at end of file diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml deleted file mode 100644 index a23e90b..0000000 --- a/.github/workflows/quality.yml +++ /dev/null @@ -1,134 +0,0 @@ -name: Code Quality - -on: - push: - branches: [ master, main ] - pull_request: - branches: [ master, main ] - -jobs: - lint: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - clang-format \ - cppcheck \ - shellcheck \ - libxml2-utils \ - desktop-file-utils - - - name: Check C/C++ code formatting - run: | - # Find all C/C++ files and check formatting - echo "::warning::Code formatting check - this is informational only" - find src/ -name "*.c" -o -name "*.h" | while read file; do - echo "Checking format of $file" - if ! clang-format --dry-run "$file" > /dev/null 2>&1; then - echo "::warning file=$file::Code formatting issues found in $file" - fi - done || true - - - name: Run cppcheck - run: | - echo "::warning::Running cppcheck analysis - issues will be reported as warnings" - cppcheck --enable=warning,style,performance,portability,information \ - --suppress=missingIncludeSystem \ - --suppress=unusedFunction \ - --inline-suppr \ - --template='{file}:{line}: {severity}: {message}' \ - src/ 2>&1 | while IFS= read -r line; do - if [[ $line =~ ^(.+):([0-9]+):[[:space:]]*([^:]+):[[:space:]]*(.+)$ ]]; then - file="${BASH_REMATCH[1]}" - lineno="${BASH_REMATCH[2]}" - severity="${BASH_REMATCH[3]}" - message="${BASH_REMATCH[4]}" - echo "::warning file=$file,line=$lineno::$severity: $message" - else - echo "$line" - fi - done || true - - - name: Check shell scripts - run: | - echo "::warning::Running shellcheck analysis - issues will be reported as warnings" - find . -name "*.sh" -type f | while read -r file; do - echo "Checking shell script: $file" - if ! shellcheck "$file"; then - echo "::warning file=$file::Shellcheck found issues in $file" - fi - done || true - - - name: Validate desktop files - run: | - echo "::warning::Validating desktop files - issues will be reported as warnings" - find . -name "*.desktop.in" -type f | while read -r file; do - echo "Validating $file" - # Basic validation - desktop-file-validate would need the processed .desktop file - if ! xmllint --noout --nonet --quiet "$file" 2>/dev/null; then - echo "::warning file=$file::Desktop file validation issues found in $file" - fi - done || true - - - name: Check XML files - run: | - echo "::warning::Validating XML files - issues will be reported as warnings" - find . -name "*.xml" -type f | while read -r file; do - echo "Validating XML: $file" - if ! xmllint --noout "$file" 2>/dev/null; then - echo "::warning file=$file::XML validation issues found in $file" - fi - done || true - - - name: Check for common issues - run: | - echo "::warning::Checking for common code issues - issues will be reported as warnings" - - # Check for trailing whitespace - if grep -r '[[:space:]]$' src/ --exclude-dir=.git; then - echo "::warning::Found trailing whitespace in source files" - fi - - # Check for tabs in source files (if project prefers spaces) - if grep -r $'\t' src/ --include="*.c" --include="*.h" --exclude-dir=.git; then - echo "::warning::Found tabs in source files - consider using spaces for consistency" - fi - - # Always succeed - true - - security: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Run security checks - run: | - echo "::warning::Running security analysis - issues will be reported as warnings" - echo "Checking for potential security issues..." - - # Look for dangerous functions - if grep -r '\(strcpy\|sprintf\|gets\|strcat\)(' src/ --include="*.c"; then - echo "::warning::Found potentially unsafe functions - consider using safer alternatives" - fi - - # Check for TODO/FIXME comments that might indicate security issues - if grep -r 'TODO.*\(security\|vulner\|exploit\)' src/; then - echo "::warning::Found security-related TODO comments" - fi - - if grep -r 'FIXME.*\(security\|vulner\|exploit\)' src/; then - echo "::warning::Found security-related FIXME comments" - fi - - echo "Security check completed" - # Always succeed - true
\ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a6cfbb4..706a5a2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,117 +1,25 @@ -name: Release - +name: Release Version on: push: tags: - - 'v*' + - "v*.*.*" jobs: release: + name: Release New Version runs-on: ubuntu-latest - container: - image: 'fedora:latest' - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Install dependencies - run: | - dnf update -y - dnf install -y \ - autoconf-archive \ - clang \ - clang-analyzer \ - bison \ - desktop-file-utils \ - flex \ - gcc \ - git \ - gmp-devel \ - gtk3 \ - libmpc-devel \ - libxml2-devel \ - make \ - mate-common \ - mate-desktop-devel \ - mpfr-devel \ - redhat-rpm-config \ - curl \ - which \ - sha256sum - - - name: Set up environment variables - run: | - export CPU_COUNT=$(nproc) - echo "CPU_COUNT=$CPU_COUNT" >> $GITHUB_ENV - echo "REPO_NAME=mate-calc" >> $GITHUB_ENV - echo "OWNER_NAME=mate-desktop" >> $GITHUB_ENV - - - name: Generate build system - run: | - NOCONFIGURE=1 ./autogen.sh - - - name: Configure - run: | - ./configure --enable-compile-warnings=maximum - - - name: Build - run: | - make -j $CPU_COUNT - - - name: Create distribution archive - run: | - make distcheck + - name: Repository checkout + uses: actions/checkout@v5 - - name: Generate checksums - run: | - for file in mate-calc-*.tar.xz; do - if [ -f "$file" ]; then - sha256sum "$file" > "$file.sha256" - fi - done + - name: Install dependency packages + run: sudo .github/workflows/ubuntu.sh - - name: Notify release servers - run: | - # Notify MATE release servers (if configured) - if [ -n "${{ secrets.RELEASE_NOTIFY_TOKEN }}" ]; then - curl -X POST -H "Authorization: token ${{ secrets.RELEASE_NOTIFY_TOKEN }}" \ - -d '{"tag":"${{ github.ref_name }}","repo":"mate-calc"}' \ - https://release.mate-desktop.org/release || echo "Release notification failed" - fi + - name: Build the source code + run: .github/workflows/builds.sh autotools - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - draft: false - prerelease: false - generate_release_notes: true - files: | - mate-calc-*.tar.xz - mate-calc-*.tar.xz.sha256 - body: | - Release ${{ github.ref_name }} of MATE Calculator - - This release includes the source distribution archive and checksums. - - ## Installation - - Download the `mate-calc-*.tar.xz` file and verify it with the corresponding `.sha256` checksum file: - - ```bash - sha256sum -c mate-calc-*.tar.xz.sha256 - ``` - - Then extract and build: - - ```bash - tar -xf mate-calc-*.tar.xz - cd mate-calc-* - ./configure - make - sudo make install - ``` - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file + - name: Create github release + run: | + gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --generate-notes mate-calc-*.tar.xz + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ubuntu.sh b/.github/workflows/ubuntu.sh new file mode 100755 index 0000000..3792bd3 --- /dev/null +++ b/.github/workflows/ubuntu.sh @@ -0,0 +1,43 @@ +#!/usr/bin/bash + +# Use grouped output messages +infobegin() { + echo "::group::${1}" +} +infoend() { + echo "::endgroup::" +} + +# Required packages on Ubuntu +requires=( + ccache # Use ccache to speed up build + meson # Used for meson build +) + +# https://git.launchpad.net/ubuntu/+source/mate-calc/tree/debian/control +requires+=( + autopoint + bison + flex + git + libatk1.0-dev + libglib2.0-dev + libgmp-dev + libgtk-3-dev + libmpc-dev + libmpfr-dev + libxml2-dev + make + mate-common + yelp-tools +) + +infobegin "Update system" +apt-get update -y +infoend + +infobegin "Install dependency packages" +env DEBIAN_FRONTEND=noninteractive \ + apt-get install --assume-yes \ + ${requires[@]} +infoend diff --git a/configure.ac b/configure.ac index 5e1b579..443dd40 100644 --- a/configure.ac +++ b/configure.ac @@ -4,6 +4,7 @@ AC_INIT([mate-calc], [1.28.0], [https://github.com/mate-desktop/mate-calc/issues [mate-calc], [https://mate-desktop.org]) AM_INIT_AUTOMAKE([1.9 foreign no-dist-gzip dist-xz check-news]) +AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS(config.h) AM_MAINTAINER_MODE MATE_MAINTAINER_MODE_DEFINES diff --git a/src/Makefile.am b/src/Makefile.am index 4ef161a..5a5ea4c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,6 +16,8 @@ nodist_mate_calc_SOURCES= $(BUILT_SOURCES) mate_calc_SOURCES = \ mate-calc.c \ + mate-calc-application.c \ + mate-calc-application.h \ currency.c \ currency.h \ currency-manager.c \ diff --git a/src/lexer.c b/src/lexer.c index 10bfe36..4a05d65 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -441,78 +441,102 @@ LETTER_STATE: } else if(type == PL_SUB_DIGIT) { + LexerToken* ret; + gchar* sub; while(pl_get_next_token(state) == PL_SUB_DIGIT); pl_roll_back(state); - tmp = g_ascii_strdown(pl_get_marked_substring(state), -1); + sub = pl_get_marked_substring(state); + tmp = g_ascii_strdown(sub, -1); + g_free(sub); if(g_strcmp0(tmp, "mod") == 0) { + g_free(tmp); return l_insert_token(lstate, T_MOD); } if(g_strcmp0(tmp, "and") == 0) { + g_free(tmp); return l_insert_token(lstate, T_AND); } if(g_strcmp0(tmp, "or") == 0) { + g_free(tmp); return l_insert_token(lstate, T_OR); } if(g_strcmp0(tmp, "xor") == 0) { + g_free(tmp); return l_insert_token(lstate, T_XOR); } if(g_strcmp0(tmp, "not") == 0) { + g_free(tmp); return l_insert_token(lstate, T_NOT); } if(g_strcmp0(tmp, "in") == 0) { + g_free(tmp); return l_insert_token(lstate, T_IN); } + g_free(tmp); if(l_check_if_function(lstate)) { - return l_insert_token(lstate, T_FUNCTION); + ret = l_insert_token(lstate, T_FUNCTION); } else { - return l_insert_token(lstate, T_VARIABLE); + ret = l_insert_token(lstate, T_VARIABLE); } + return ret; } else { + LexerToken* ret; + gchar* sub; pl_roll_back(state); - tmp = g_ascii_strdown(pl_get_marked_substring(state), -1); + sub = pl_get_marked_substring(state); + tmp = g_ascii_strdown(sub, -1); + g_free(sub); if(g_strcmp0(tmp, "mod") == 0) { + g_free(tmp); return l_insert_token(lstate, T_MOD); } if(g_strcmp0(tmp, "and") == 0) { + g_free(tmp); return l_insert_token(lstate, T_AND); } if(g_strcmp0(tmp, "or") == 0) { + g_free(tmp); return l_insert_token(lstate, T_OR); } if(g_strcmp0(tmp, "xor") == 0) { + g_free(tmp); return l_insert_token(lstate, T_XOR); } if(g_strcmp0(tmp, "not") == 0) { + g_free(tmp); return l_insert_token(lstate, T_NOT); } if(g_strcmp0(tmp, "in") == 0) { + g_free(tmp); return l_insert_token(lstate, T_IN); } + g_free(tmp); if(l_check_if_function(lstate)) { - return l_insert_token(lstate, T_FUNCTION); + ret = l_insert_token(lstate, T_FUNCTION); } else { - return l_insert_token(lstate, T_VARIABLE); + ret = l_insert_token(lstate, T_VARIABLE); } + return ret; } } } diff --git a/src/mate-calc-application.c b/src/mate-calc-application.c new file mode 100644 index 0000000..587bb27 --- /dev/null +++ b/src/mate-calc-application.c @@ -0,0 +1,227 @@ +/* + * mate-calc-application.c - GtkApplication for MATE Calculator + * + * Copyright (C) 2026 MATE Desktop Team + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 2 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. + */ + +#include <config.h> + +#include <string.h> +#include <glib/gi18n.h> + +#include "mate-calc-application.h" +#include "math-window.h" +#include "mp-equation.h" +#include "unit-manager.h" + +/* Global settings variable for backward compatibility with existing code */ +GSettings *g_settings_var = NULL; + +struct _MateCalcApplication +{ + GtkApplication parent_instance; + + GSettings *settings; +}; + +G_DEFINE_TYPE(MateCalcApplication, mate_calc_application, GTK_TYPE_APPLICATION) + +static void +mate_calc_application_startup(GApplication *application) +{ + MateCalcApplication *app = MATE_CALC_APPLICATION(application); + + G_APPLICATION_CLASS(mate_calc_application_parent_class)->startup(application); + + /* Initialize settings */ + app->settings = g_settings_new("org.mate.calc"); + g_settings_var = app->settings; +} + +static void +mate_calc_application_activate(GApplication *application) +{ + MateCalcApplication *app = MATE_CALC_APPLICATION(application); + MathEquation *equation; + GtkWindow *window; + MathButtons *buttons; + gint accuracy, word_size, base; + gboolean show_tsep, show_zeroes, show_hist; + MpDisplayFormat number_format; + MPAngleUnit angle_units; + ButtonMode button_mode; + gchar *source_currency, *target_currency; + gchar *source_units, *target_units; + + accuracy = g_settings_get_int(app->settings, "accuracy"); + word_size = g_settings_get_int(app->settings, "word-size"); + base = g_settings_get_int(app->settings, "base"); + show_tsep = g_settings_get_boolean(app->settings, "show-thousands"); + show_zeroes = g_settings_get_boolean(app->settings, "show-zeroes"); + show_hist = g_settings_get_boolean(app->settings, "show-history"); + number_format = g_settings_get_enum(app->settings, "number-format"); + angle_units = g_settings_get_enum(app->settings, "angle-units"); + button_mode = g_settings_get_enum(app->settings, "button-mode"); + source_currency = g_settings_get_string(app->settings, "source-currency"); + target_currency = g_settings_get_string(app->settings, "target-currency"); + source_units = g_settings_get_string(app->settings, "source-units"); + target_units = g_settings_get_string(app->settings, "target-units"); + + equation = math_equation_new(); + math_equation_set_accuracy(equation, accuracy); + math_equation_set_word_size(equation, word_size); + math_equation_set_show_thousands_separators(equation, show_tsep); + math_equation_set_show_trailing_zeroes(equation, show_zeroes); + math_equation_set_number_format(equation, number_format); + math_equation_set_angle_units(equation, angle_units); + math_equation_set_source_currency(equation, source_currency); + math_equation_set_target_currency(equation, target_currency); + math_equation_set_source_units(equation, source_units); + math_equation_set_target_units(equation, target_units); + g_free(source_currency); + g_free(target_currency); + g_free(source_units); + g_free(target_units); + + window = GTK_WINDOW(math_window_new(equation)); + gtk_application_add_window(GTK_APPLICATION(app), window); + + buttons = math_window_get_buttons(MATH_WINDOW(window)); + + math_window_set_show_history(MATH_WINDOW(window), show_hist); + math_buttons_set_programming_base(buttons, base); + math_buttons_set_mode(buttons, button_mode); + + gtk_widget_show(GTK_WIDGET(window)); +} + +static void +mate_calc_application_shutdown(GApplication *application) +{ + MateCalcApplication *app = MATE_CALC_APPLICATION(application); + + g_settings_var = NULL; + g_clear_object(&app->settings); + + G_APPLICATION_CLASS(mate_calc_application_parent_class)->shutdown(application); +} + +static int +do_convert(const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z, void *data) +{ + return unit_manager_convert_by_symbol(unit_manager_get_default(), x, x_units, z_units, z); +} + +static gint +mate_calc_application_handle_local_options(GApplication *application, + GVariantDict *options) +{ + if (g_variant_dict_contains(options, "version")) + { + g_print("%s %s\n", "mate-calc", VERSION); + return 0; + } + + const gchar *solve_expr = NULL; + if (g_variant_dict_lookup(options, "solve", "&s", &solve_expr)) + { + /* Solve mode - handled locally without initializing the full GUI */ + MPEquationOptions mp_options; + MPErrorCode error; + MPNumber result = mp_new(); + char *result_str; + + memset(&mp_options, 0, sizeof(mp_options)); + mp_options.base = 10; + mp_options.wordlen = 32; + mp_options.angle_units = MP_DEGREES; + mp_options.convert = do_convert; + + error = mp_equation_parse(solve_expr, &mp_options, &result, NULL); + + if (error == PARSER_ERR_MP) + { + g_printerr("Error: %s\n", mp_get_error()); + mp_clear(&result); + return 1; + } + else if (error != 0) + { + g_printerr("Error: %s\n", mp_error_code_to_string(error)); + mp_clear(&result); + return 1; + } + else + { + MpSerializer *serializer = mp_serializer_new(MP_DISPLAY_FORMAT_AUTOMATIC, 10, 9); + result_str = mp_serializer_to_string(serializer, &result); + g_print("%s\n", result_str); + g_free(result_str); + g_object_unref(serializer); + mp_clear(&result); + return 0; + } + } + + /* Let the default handler continue */ + return -1; +} + +static gint +mate_calc_application_command_line(GApplication *application, + GApplicationCommandLine *command_line) +{ + /* Activate normal GUI mode */ + g_application_activate(application); + + return 0; +} + +static void +mate_calc_application_init(MateCalcApplication *app) +{ + /* Command line options */ + const GOptionEntry options[] = { + { "version", 'v', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, + N_("Show release version"), NULL }, + { "solve", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, NULL, + N_("Solve the given equation"), N_("EQUATION") }, + { NULL } + }; + + g_application_add_main_option_entries(G_APPLICATION(app), options); +} + +static void +mate_calc_application_class_init(MateCalcApplicationClass *klass) +{ + GApplicationClass *app_class = G_APPLICATION_CLASS(klass); + + app_class->startup = mate_calc_application_startup; + app_class->activate = mate_calc_application_activate; + app_class->shutdown = mate_calc_application_shutdown; + app_class->handle_local_options = mate_calc_application_handle_local_options; + app_class->command_line = mate_calc_application_command_line; +} + +MateCalcApplication * +mate_calc_application_new(void) +{ + return g_object_new(MATE_CALC_TYPE_APPLICATION, + "application-id", "org.mate.Calculator", + "flags", G_APPLICATION_HANDLES_COMMAND_LINE, + NULL); +} + +GSettings * +mate_calc_application_get_settings(MateCalcApplication *app) +{ + g_return_val_if_fail(MATE_CALC_IS_APPLICATION(app), NULL); + return app->settings; +} diff --git a/src/mate-calc-application.h b/src/mate-calc-application.h new file mode 100644 index 0000000..b8ec26d --- /dev/null +++ b/src/mate-calc-application.h @@ -0,0 +1,44 @@ +/* + * mate-calc-application.h - GtkApplication for MATE Calculator + * + * Copyright (C) 2026 MATE Desktop Team + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 2 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. + */ + +#ifndef MATE_CALC_APPLICATION_H +#define MATE_CALC_APPLICATION_H + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define MATE_CALC_TYPE_APPLICATION (mate_calc_application_get_type()) +G_DECLARE_FINAL_TYPE(MateCalcApplication, mate_calc_application, MATE_CALC, APPLICATION, GtkApplication) + +/** + * mate_calc_application_new: + * + * Creates a new MateCalcApplication instance. + * + * Returns: (transfer full): A new MateCalcApplication + */ +MateCalcApplication *mate_calc_application_new(void); + +/** + * mate_calc_application_get_settings: + * @app: A MateCalcApplication + * + * Gets the GSettings object for the calculator. + * + * Returns: (transfer none): The GSettings object + */ +GSettings *mate_calc_application_get_settings(MateCalcApplication *app); + +G_END_DECLS + +#endif /* MATE_CALC_APPLICATION_H */ diff --git a/src/mate-calc-cmd.c b/src/mate-calc-cmd.c index 842eab5..a13c4aa 100644 --- a/src/mate-calc-cmd.c +++ b/src/mate-calc-cmd.c @@ -91,6 +91,7 @@ main(void) solve(equation); } free(equation); + g_object_unref(result_serializer); return 0; } diff --git a/src/mate-calc.c b/src/mate-calc.c index da95462..dfeb726 100644 --- a/src/mate-calc.c +++ b/src/mate-calc.c @@ -1,6 +1,7 @@ /* * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (C) 2008-2011 Robert Ancell + * Copyright (C) 2011-2026 MATE Desktop Team * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software @@ -9,251 +10,33 @@ * license. */ -#ifdef HAVE_CONFIG_H #include <config.h> -#endif #include <stdlib.h> -#include <stdio.h> -#include <string.h> #include <locale.h> #include <glib/gi18n.h> -#include "math-window.h" -#include "math-preferences.h" -#include "mp-equation.h" -#include "unit-manager.h" -#include "utility.h" +#include "mate-calc-application.h" -GSettings *g_settings_var = NULL; - -static MathWindow *window; - -static void -version(const gchar *progname) -{ - /* NOTE: Is not translated so can be easily parsed */ - fprintf(stderr, "%1$s %2$s\n", progname, VERSION); -} - -static int -do_convert(const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z, void *data) -{ - return unit_manager_convert_by_symbol(unit_manager_get_default(), x, x_units, z_units, z); -} - -static void -solve(const char *equation) -{ - MPEquationOptions options; - MPErrorCode error; - MPNumber result = mp_new(); - char *result_str; - - memset(&options, 0, sizeof(options)); - options.base = 10; - options.wordlen = 32; - options.angle_units = MP_DEGREES; - options.convert = do_convert; - - error = mp_equation_parse(equation, &options, &result, NULL); - if(error == PARSER_ERR_MP) { - fprintf(stderr, "Error: %s\n", mp_get_error()); - mp_clear(&result); - exit(1); - } - else if(error != 0) { - fprintf(stderr, "Error: %s\n", mp_error_code_to_string(error)); - mp_clear(&result); - exit(1); - } - else { - result_str = mp_serializer_to_string(mp_serializer_new(MP_DISPLAY_FORMAT_AUTOMATIC, 10, 9), &result); - printf("%s\n", result_str); - mp_clear(&result); - exit(0); - } -} - -static void -usage(const gchar *progname, gboolean show_application, gboolean show_gtk) -{ - fprintf(stderr, - /* Description on how to use mate-calc displayed on command-line */ - _("Usage:\n" - " %s — Perform mathematical calculations"), progname); - - fprintf(stderr, - "\n\n"); - - fprintf(stderr, - /* Description on mate-calc command-line help options displayed on command-line */ - _("Help Options:\n" - " -v, --version Show release version\n" - " -h, -?, --help Show help options\n" - " --help-all Show all help options\n" - " --help-gtk Show GTK+ options")); - fprintf(stderr, - "\n\n"); - - if (show_gtk) { - fprintf(stderr, - /* Description on mate-calc command-line GTK+ options displayed on command-line */ - _("GTK+ Options:\n" - " --class=CLASS Program class as used by the window manager\n" - " --name=NAME Program name as used by the window manager\n" - " --screen=SCREEN X screen to use\n" - " --sync Make X calls synchronous\n" - " --gtk-module=MODULES Load additional GTK+ modules\n" - " --g-fatal-warnings Make all warnings fatal")); - fprintf(stderr, - "\n\n"); - } - - if (show_application) { - fprintf(stderr, - /* Description on mate-calc application options displayed on command-line */ - _("Application Options:\n" - " -s, --solve <equation> Solve the given equation")); - fprintf(stderr, - "\n\n"); - } -} - -static void -get_options(int argc, char *argv[]) -{ - int i; - char *progname, *arg; - - progname = g_path_get_basename(argv[0]); - - for (i = 1; i < argc; i++) { - arg = argv[i]; - - if (strcmp(arg, "-v") == 0 || - strcmp(arg, "--version") == 0) { - version(progname); - g_free(progname); - exit(0); - } - else if (strcmp(arg, "-h") == 0 || - strcmp(arg, "-?") == 0 || - strcmp(arg, "--help") == 0) { - usage(progname, TRUE, FALSE); - g_free(progname); - exit(0); - } - else if (strcmp(arg, "--help-all") == 0) { - usage(progname, TRUE, TRUE); - g_free(progname); - exit(0); - } - else if (strcmp(arg, "--help-gtk") == 0) { - usage(progname, FALSE, TRUE); - g_free(progname); - exit(0); - } - else if (strcmp(arg, "-s") == 0 || - strcmp(arg, "--solve") == 0) { - i++; - if (i >= argc) { - fprintf(stderr, - /* Error printed to stderr when user uses --solve argument without an equation */ - _("Argument --solve requires an equation to solve")); - fprintf(stderr, "\n"); - g_free(progname); - exit(1); - } - else - solve(argv[i]); - } - else { - fprintf(stderr, - /* Error printed to stderr when user provides an unknown command-line argument */ - _("Unknown argument '%s'"), arg); - fprintf(stderr, "\n"); - usage(progname, TRUE, FALSE); - g_free(progname); - exit(1); - } - } - - g_free(progname); -} - -static void -quit_cb(MathWindow *win) +int +main(int argc, char **argv) { - gtk_main_quit(); -} - -int main(int argc, char **argv) -{ - MathEquation *equation; - MathButtons *buttons; - int accuracy = 9, word_size = 64, base = 10; - gboolean show_tsep = FALSE, show_zeroes = FALSE, show_hist = FALSE; - MpDisplayFormat number_format; - MPAngleUnit angle_units; - ButtonMode button_mode; - gchar *source_currency, *target_currency; - gchar *source_units, *target_units; + MateCalcApplication *app; + int status; + /* Locale setup */ setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE); - /* Seed random number generator. */ - srand48((long) time((time_t *) 0)); - - gtk_init(&argc, &argv); - - g_settings_var = g_settings_new ("org.mate.calc"); - accuracy = g_settings_get_int(g_settings_var, "accuracy"); - word_size = g_settings_get_int(g_settings_var, "word-size"); - base = g_settings_get_int(g_settings_var, "base"); - show_tsep = g_settings_get_boolean(g_settings_var, "show-thousands"); - show_zeroes = g_settings_get_boolean(g_settings_var, "show-zeroes"); - show_hist = g_settings_get_boolean(g_settings_var, "show-history"); - number_format = g_settings_get_enum(g_settings_var, "number-format"); - angle_units = g_settings_get_enum(g_settings_var, "angle-units"); - button_mode = g_settings_get_enum(g_settings_var, "button-mode"); - source_currency = g_settings_get_string(g_settings_var, "source-currency"); - target_currency = g_settings_get_string(g_settings_var, "target-currency"); - source_units = g_settings_get_string(g_settings_var, "source-units"); - target_units = g_settings_get_string(g_settings_var, "target-units"); - - equation = math_equation_new(); - math_equation_set_accuracy(equation, accuracy); - math_equation_set_word_size(equation, word_size); - math_equation_set_show_thousands_separators(equation, show_tsep); - math_equation_set_show_trailing_zeroes(equation, show_zeroes); - math_equation_set_number_format(equation, number_format); - math_equation_set_angle_units(equation, angle_units); - math_equation_set_source_currency(equation, source_currency); - math_equation_set_target_currency(equation, target_currency); - math_equation_set_source_units(equation, source_units); - math_equation_set_target_units(equation, target_units); - g_free(source_currency); - g_free(target_currency); - g_free(source_units); - g_free(target_units); - - get_options(argc, argv); - - //gtk_window_set_default_icon_name("accessories-calculator"); - - window = math_window_new(equation); - buttons = math_window_get_buttons(window); - g_signal_connect(G_OBJECT(window), "quit", G_CALLBACK(quit_cb), NULL); - math_window_set_show_history(window, show_hist); - math_buttons_set_programming_base(buttons, base); - math_buttons_set_mode(buttons, button_mode); // FIXME: We load the basic buttons even if we immediately switch to the next type + /* Seed random number generator */ + srand48((long)time((time_t *)0)); - gtk_widget_show(GTK_WIDGET(window)); - gtk_main(); + /* Create and run application */ + app = mate_calc_application_new(); + status = g_application_run(G_APPLICATION(app), argc, argv); + g_object_unref(app); - return 0; + return status; } diff --git a/src/math-equation.c b/src/math-equation.c index 2ce3ab8..1faee23 100644 --- a/src/math-equation.c +++ b/src/math-equation.c @@ -803,9 +803,29 @@ math_equation_get_equation(MathEquation *equation) continue; } - /* Ignore thousands separators */ - if (c == mp_serializer_get_thousands_separator(equation->priv->serializer) && last_is_digit && next_is_digit) - ; + /* Ignore thousands separators (including spaces between digits) */ + if (c == mp_serializer_get_thousands_separator(equation->priv->serializer) && last_is_digit && next_is_digit) { + /* Skip official thousands separator */ + } + else if (c == ' ' && last_is_digit) { + /* Skip consecutive spaces between digits - look ahead to find next non-space character */ + const gchar *lookahead = read_iter; + while (*lookahead != '\0' && g_utf8_get_char(lookahead) == ' ') { + lookahead = g_utf8_next_char(lookahead); + } + /* Only skip if the next non-space character is a digit */ + if (*lookahead != '\0' && g_unichar_isdigit(g_utf8_get_char(lookahead))) { + /* Skip all consecutive spaces - advance read_iter to the last space */ + while (g_utf8_get_char(g_utf8_next_char(read_iter)) == ' ') { + read_iter = g_utf8_next_char(read_iter); + offset++; + } + /* Current space will be skipped by not appending it */ + } else { + /* Keep this space - it's not between digits */ + g_string_append_unichar(eq_text, c); + } + } /* Substitute radix character */ else if (c == mp_serializer_get_radix(equation->priv->serializer) && (last_is_digit || next_is_digit)) g_string_append_unichar(eq_text, '.'); diff --git a/src/math-window.c b/src/math-window.c index 320f83b..7e021fb 100644 --- a/src/math-window.c +++ b/src/math-window.c @@ -1,6 +1,7 @@ /* * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (C) 2008-2011 Robert Ancell. + * Copyright (C) 2011-2026 MATE Desktop Team * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software @@ -16,6 +17,7 @@ #include <glib/gi18n.h> #include <gtk/gtk.h> #include <gdk/gdkkeysyms.h> +#include <mpc.h> #include "math-window.h" #include "math-history.h" @@ -57,15 +59,7 @@ struct MathWindowPrivate GtkWidget *view_history_menu_item; }; -G_DEFINE_TYPE_WITH_PRIVATE (MathWindow, math_window, GTK_TYPE_WINDOW); - -enum -{ - QUIT, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0, }; +G_DEFINE_TYPE_WITH_PRIVATE(MathWindow, math_window, GTK_TYPE_APPLICATION_WINDOW); MathWindow * math_window_new(MathEquation *equation) @@ -135,22 +129,28 @@ void math_window_critical_error(MathWindow *window, const gchar *title, const gchar *contents) { GtkWidget *dialog; + GtkApplication *app; g_return_if_fail(window != NULL); g_return_if_fail(title != NULL); g_return_if_fail(contents != NULL); - dialog = gtk_message_dialog_new(NULL, 0, + dialog = gtk_message_dialog_new(GTK_WINDOW(window), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, "%s", title); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", contents); - gtk_dialog_add_buttons(GTK_DIALOG(dialog), "gtk-quit", GTK_RESPONSE_ACCEPT, NULL); + gtk_dialog_add_buttons(GTK_DIALOG(dialog), _("_Quit"), GTK_RESPONSE_ACCEPT, NULL); gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); - g_signal_emit(window, signals[QUIT], 0); + /* Critical error - quit the entire application */ + app = gtk_window_get_application(GTK_WINDOW(window)); + if (app) + g_application_quit(G_APPLICATION(app)); } static void mode_changed_cb(GtkWidget *menu, MathWindow *window) @@ -198,64 +198,6 @@ static void show_preferences_cb(GtkMenuItem *menu, MathWindow *window) gtk_window_present(GTK_WINDOW(window->priv->preferences_dialog)); } -static gboolean -key_press_cb(MathWindow *window, GdkEventKey *event) -{ - gboolean result; - g_signal_emit_by_name(window->priv->display, "key-press-event", event, &result); - - /* Keyboard navigation for history */ - if ((event->state & GDK_MOD1_MASK) == GDK_MOD1_MASK && (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down)) - { - switch (event->keyval) - { - case GDK_KEY_Up: - math_history_set_current(window->priv->history, -1); - break; - case GDK_KEY_Down: - math_history_set_current(window->priv->history, 1); - break; - } - - MathHistoryEntry *entry = math_history_get_entry_at(window->priv->history, math_history_get_current(window->priv->history)); - if (entry) - { - gchar *equation_string = math_history_entry_get_equation(entry); - math_equation_set(window->priv->equation, equation_string); - g_free(equation_string); - } - return TRUE; - } - else if (math_buttons_get_mode (window->priv->buttons) == PROGRAMMING && (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) { - switch(event->keyval) - { - /* Binary */ - case GDK_KEY_b: - math_equation_set_base (window->priv->equation, 2); - return TRUE; - /* Octal */ - case GDK_KEY_o: - math_equation_set_base (window->priv->equation, 8); - return TRUE; - /* Decimal */ - case GDK_KEY_d: - math_equation_set_base (window->priv->equation, 10); - return TRUE; - /* Hexdecimal */ - case GDK_KEY_h: - math_equation_set_base (window->priv->equation, 16); - return TRUE; - } - } - - return result; -} - -static void delete_cb(MathWindow *window, GdkEvent *event) -{ - g_signal_emit(window, signals[QUIT], 0); -} - static void copy_cb(GtkWidget *widget, MathWindow *window) { math_equation_copy(window->priv->equation); @@ -384,6 +326,65 @@ static void about_cb(GtkWidget* widget, MathWindow* window) g_free (license_trans); } +static void quit_cb(GtkWidget* widget, MathWindow* window) +{ + gtk_widget_destroy(GTK_WIDGET(window)); +} + +static gboolean +key_press_cb(MathWindow *window, GdkEventKey *event) +{ + gboolean result; + g_signal_emit_by_name(window->priv->display, "key-press-event", event, &result); + + /* Keyboard navigation for history */ + if ((event->state & GDK_MOD1_MASK) == GDK_MOD1_MASK && (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down)) + { + switch (event->keyval) + { + case GDK_KEY_Up: + math_history_set_current(window->priv->history, -1); + break; + case GDK_KEY_Down: + math_history_set_current(window->priv->history, 1); + break; + } + + MathHistoryEntry *entry = math_history_get_entry_at(window->priv->history, math_history_get_current(window->priv->history)); + if (entry) + { + gchar *equation_string = math_history_entry_get_equation(entry); + math_equation_set(window->priv->equation, equation_string); + g_free(equation_string); + } + return TRUE; + } + else if (math_buttons_get_mode (window->priv->buttons) == PROGRAMMING && (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) { + switch(event->keyval) + { + /* Binary */ + case GDK_KEY_b: + math_equation_set_base (window->priv->equation, 2); + return TRUE; + /* Octal */ + case GDK_KEY_o: + math_equation_set_base (window->priv->equation, 8); + return TRUE; + /* Decimal */ + case GDK_KEY_d: + math_equation_set_base (window->priv->equation, 10); + return TRUE; + /* Hexdecimal */ + case GDK_KEY_h: + math_equation_set_base (window->priv->equation, 16); + return TRUE; + } + } + + return result; +} + + static void scroll_changed_cb(GtkAdjustment *adjustment, MathWindow *window) { @@ -454,11 +455,6 @@ history_set_serializer_cb(MathEquation *equation, MathWindow *window) math_history_set_serializer(window->priv->history, serializer); } -static void quit_cb(GtkWidget* widget, MathWindow* window) -{ - g_signal_emit(window, signals[QUIT], 0); -} - static GtkWidget *add_menu_item(GtkWidget *menu, GtkWidget *menu_item, GCallback callback, gpointer callback_data) { gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); @@ -697,14 +693,6 @@ math_window_class_init(MathWindowClass *klass) "Show-history", FALSE, G_PARAM_READWRITE)); - - signals[QUIT] = g_signal_new("quit", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MathWindowClass, quit), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); } static void @@ -718,5 +706,4 @@ math_window_init(MathWindow *window) gtk_window_set_role(GTK_WINDOW(window), "mate-calc"); gtk_window_set_resizable(GTK_WINDOW(window), FALSE); g_signal_connect_after(G_OBJECT(window), "key-press-event", G_CALLBACK(key_press_cb), NULL); - g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(delete_cb), NULL); } diff --git a/src/math-window.h b/src/math-window.h index 72f19bc..0a8f97f 100644 --- a/src/math-window.h +++ b/src/math-window.h @@ -1,6 +1,7 @@ /* * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (C) 2008-2011 Robert Ancell. + * Copyright (C) 2011-2026 MATE Desktop Team * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software @@ -12,7 +13,7 @@ #ifndef MATH_WINDOW_H #define MATH_WINDOW_H -#include <glib-object.h> +#include <gtk/gtk.h> #include "math-equation.h" #include "math-display.h" #include "math-buttons.h" @@ -26,15 +27,13 @@ typedef struct MathWindowPrivate MathWindowPrivate; typedef struct { - GtkWindow parent_instance; + GtkApplicationWindow parent_instance; MathWindowPrivate *priv; } MathWindow; typedef struct { - GtkWindowClass parent_class; - - void (*quit) (MathWindow *window); + GtkApplicationWindowClass parent_class; } MathWindowClass; GType math_window_get_type(void); diff --git a/src/meson.build b/src/meson.build index bb183cd..f6a5396 100644 --- a/src/meson.build +++ b/src/meson.build @@ -25,6 +25,7 @@ ui_resources = gnome.compile_resources( src += [ 'mate-calc.c', + 'mate-calc-application.c', 'currency-manager.c', 'currency.c', 'financial.c', diff --git a/src/mp-equation.c b/src/mp-equation.c index 0c9e204..ad2ab39 100644 --- a/src/mp-equation.c +++ b/src/mp-equation.c @@ -291,11 +291,11 @@ mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber * int ret; int err; ParserState* state; - state = p_create_parser (expression, options); - if (!(expression && result) || strlen(expression) == 0) return PARSER_ERR_INVALID; + state = p_create_parser (expression, options); + state->variable_is_defined = variable_is_defined; state->get_variable = get_variable; state->set_variable = set_variable; @@ -307,6 +307,7 @@ mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber * ret = p_parse (state); if (state->error_token != NULL && error_token != NULL) { *error_token = state->error_token; + state->error_token = NULL; } /* Error during parsing */ if (state->error) { diff --git a/src/parser.c b/src/parser.c index d60c142..6aeba9e 100644 --- a/src/parser.c +++ b/src/parser.c @@ -238,6 +238,7 @@ p_create_parser(const gchar* input, MPEquationOptions* options) state->options = options; state->error = 0; state->error_token = NULL; + state->ret = mp_new(); return state; } @@ -276,7 +277,6 @@ p_parse(ParserState* state) ans = (MPNumber *) (*(state->root->evaluate))(state->root); if(ans) { - state->ret = mp_new(); mp_set_from_mp(ans, &state->ret); mp_free(ans); return PARSER_ERR_NONE; @@ -294,6 +294,8 @@ p_destroy_parser(ParserState* state) p_destroy_all_nodes(state->root); } l_destroy_lexer(state->lexer); + mp_clear(&state->ret); + free(state->error_token); free(state); } @@ -343,7 +345,7 @@ p_check_variable(ParserState* state, gchar* name) if(utf8_next_char(name)[0] != '\0') { result = 1; - buffer = (gchar*) malloc(sizeof(gchar) * strlen(name)); + buffer = (gchar*) malloc(sizeof(gchar) * (strlen(name) + 1)); for(c = name; *c != '\0'; c = next) { next = utf8_next_char(c); diff --git a/src/parserfunc.c b/src/parserfunc.c index dc28e7e..a7ea8f1 100644 --- a/src/parserfunc.c +++ b/src/parserfunc.c @@ -11,8 +11,10 @@ void set_error(ParserState* state, gint errorno, const gchar *token) { state->error = errorno; - if(token) + if(token) { + free(state->error_token); state->error_token = strdup(token); + } } /* Unused function pointer. This won't be called anytime. */ @@ -177,13 +179,17 @@ pf_get_variable(ParseNode* self) if(!(self->state->get_variable)) { - free(ans); + mp_clear(&value); + mp_clear(&t); + mp_free(ans); return NULL; } /* If defined, then get the variable */ if((*(self->state->get_variable))(self->state, self->token->string, ans)) { + mp_clear(&value); + mp_clear(&t); return ans; } @@ -191,7 +197,7 @@ pf_get_variable(ParseNode* self) if(utf8_next_char(self->token->string)[0] != '\0') { result = 1; - buffer = (gchar*) malloc(sizeof(gchar) * strlen(self->token->string)); + buffer = (gchar*) malloc(sizeof(gchar) * (strlen(self->token->string) + 1)); mp_set_from_integer(1, &value); for(c = self->token->string; *c != '\0'; c = next) { @@ -208,9 +214,11 @@ pf_get_variable(ParseNode* self) if(result) mp_set_from_mp(&value, ans); } + mp_clear(&value); + mp_clear(&t); if(!result) { - free (ans); + mp_free(ans); ans = NULL; set_error(self->state, PARSER_ERR_UNKNOWN_VARIABLE, self->token->string); } @@ -237,13 +245,17 @@ pf_get_variable_with_power(ParseNode* self) if(!(self->state->get_variable)) { - free(ans); + mp_clear(&value); + mp_clear(&t); + mp_free(ans); return NULL; } /* If defined, then get the variable */ if((*(self->state->get_variable))(self->state, self->token->string, ans)) { + mp_clear(&value); + mp_clear(&t); mp_xpowy_integer(ans, pow, ans); return ans; } @@ -252,7 +264,7 @@ pf_get_variable_with_power(ParseNode* self) if(utf8_next_char(self->token->string)[0] != '\0') { result = 1; - buffer = (gchar*) malloc(sizeof(gchar) * strlen(self->token->string)); + buffer = (gchar*) malloc(sizeof(gchar) * (strlen(self->token->string) + 1)); mp_set_from_integer(1, &value); for(c = self->token->string; *c != '\0'; c = next) { @@ -273,9 +285,11 @@ pf_get_variable_with_power(ParseNode* self) if(result) mp_set_from_mp(&value, ans); } + mp_clear(&value); + mp_clear(&t); if(!result) { - free(ans); + mp_free(ans); ans = NULL; set_error(self->state, PARSER_ERR_UNKNOWN_VARIABLE, self->token->string); } @@ -291,23 +305,23 @@ pf_apply_func(ParseNode* self) val = (MPNumber*) (*(self->right->evaluate))(self->right); if(!(self->state->get_function)) { - free(val); - free(ans); + mp_free(val); + mp_free(ans); return NULL; } if(!val) { - free(ans); + mp_free(ans); return NULL; } if(!(*(self->state->get_function))(self->state, self->token->string, val, ans)) { - free(val); - free(ans); + mp_free(val); + mp_free(ans); set_error(self->state, PARSER_ERR_UNKNOWN_FUNCTION, self->token->string); return NULL; } - free(val); + mp_free(val); return ans; } @@ -362,7 +376,7 @@ pf_apply_func_with_npower(ParseNode* self) MPNumber* ans = mp_new_ptr(); gint pow; gchar* inv_name; - inv_name = (gchar*) malloc(sizeof(gchar) * strlen(self->token->string) + strlen("⁻¹") + 1); + inv_name = (gchar*) malloc(sizeof(gchar) * (strlen(self->token->string) + strlen("⁻¹") + 1)); strcpy(inv_name, self->token->string); strcat(inv_name, "⁻¹"); val = (MPNumber*) (*(self->right->evaluate))(self->right); @@ -432,11 +446,11 @@ pf_do_sqrt(ParseNode* self) val = (MPNumber*) (*(self->right->evaluate))(self->right); if(!val) { - free(ans); + mp_free(ans); return NULL; } mp_sqrt(val, ans); - free(val); + mp_free(val); return ans; } @@ -758,7 +772,7 @@ pf_do_subtract(ParseNode* self) mp_free(left); if(right) mp_free(right); - free(ans); + mp_free(ans); return NULL; } mp_subtract(left, right, ans); @@ -782,7 +796,7 @@ pf_do_add(ParseNode* self) mp_free(left); if(right) mp_free(right); - free(ans); + mp_free(ans); return NULL; } mp_add(left, right, ans); @@ -832,7 +846,7 @@ pf_do_subtract_percent(ParseNode* self) mp_free(val); if(per) mp_free(per); - free(ans); + mp_free(ans); return NULL; } mp_add_integer(per, -100, per); @@ -947,7 +961,7 @@ pf_do_xor(ParseNode* self) mp_free(left); if(right) mp_free(right); - free(ans); + mp_free(ans); return NULL; } mp_xor(left, right, ans); |
