summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.github/workflows/archlinux.sh41
-rw-r--r--.github/workflows/build.yml163
-rwxr-xr-x.github/workflows/builds.sh64
-rw-r--r--.github/workflows/builds.yml75
-rwxr-xr-x.github/workflows/debian.sh42
-rwxr-xr-x.github/workflows/fedora.sh42
-rw-r--r--.github/workflows/notify.yml44
-rw-r--r--.github/workflows/pages.yml139
-rw-r--r--.github/workflows/quality.yml134
-rw-r--r--.github/workflows/release.yml120
-rwxr-xr-x.github/workflows/ubuntu.sh43
-rw-r--r--configure.ac1
-rw-r--r--src/Makefile.am2
-rw-r--r--src/lexer.c36
-rw-r--r--src/mate-calc-application.c227
-rw-r--r--src/mate-calc-application.h44
-rw-r--r--src/mate-calc-cmd.c1
-rw-r--r--src/mate-calc.c245
-rw-r--r--src/math-equation.c26
-rw-r--r--src/math-window.c155
-rw-r--r--src/math-window.h9
-rw-r--r--src/meson.build1
-rw-r--r--src/mp-equation.c5
-rw-r--r--src/parser.c6
-rw-r--r--src/parserfunc.c54
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);