Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,18 @@ jobs:

- name: Build wheel using cibuildwheel
run: |
cp src/spidermonkey/libjs.a src/pacparser.o src/pacparser.h src/pymod
cd src/pymod && python -m cibuildwheel --output-dir dist
cd src/pymod && python setup.py clean --all
cp -r ../quickjs ../pacparser.c ../pac_utils.h ../pacparser.h .
python -m cibuildwheel --output-dir dist
env:
CIBW_BUILD: "cp{37,38,39,310,311,312}-manylinux*64"
CIBW_ENVIRONMENT: "PACPARSER_VERSION=${{ env.PACPARSER_VERSION }}"
CIBW_BEFORE_BUILD: >-
cd {project} &&
make -C quickjs clean &&
make -C quickjs CFLAGS='-fPIC' &&
cc -g -Wall -DVERSION=$PACPARSER_VERSION -Iquickjs -fPIC -c pacparser.c -o pacparser.o &&
cp quickjs/libquickjs.a .

- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: src_changes
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/sonar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ jobs:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Install sonar-scanner and build-wrapper
uses: SonarSource/sonarcloud-github-c-cpp@44cc4d3d487fbc35e5c29b0a9d717be218d3a0e8 # v3.2.0
- name: Run sonar-scanner
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Install bear
run: sudo apt install -y bear
- name: Generate compilation database
run: |
sudo apt install -y bear
make -C src clean
bear -- make -C src
sonar-scanner
- name: SonarCloud Scan
uses: SonarSource/sonarqube-scan-action@v5.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ src/pactester
*.json
pactester
info.plist

.gemini/tmp
72 changes: 72 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Pacparser is a C library (with Python bindings) for parsing proxy auto-config (PAC) files. It embeds QuickJS JavaScript engine to evaluate PAC scripts and implements the standard PAC helper functions (e.g., `dnsDomainIs`, `isInNet`, `myIpAddress`). Licensed under LGPL.

## Build Commands

All build commands run from the repo root using `make -C src`.

```bash
# Build C library and pactester CLI (also runs tests)
make -C src

# Build without internet-dependent tests
NO_INTERNET=1 make -C src

# Build Python module (also runs Python tests)
make -C src pymod

# Install C library and pactester
sudo make -C src install

# Install Python module
sudo make -C src install-pymod

# Clean all build artifacts
make -C src clean

# Windows build (requires MinGW/MSYS2)
make -C src -f Makefile.win32
```

## Testing

Tests run automatically as part of the build (`make -C src` runs `testpactester` target).

```bash
# Run C tests manually
../tests/runtests.sh # from src/
NO_INTERNET=1 ../tests/runtests.sh # skip DNS-dependent tests

# Run Python tests manually
python ../tests/runtests.py # from src/
NO_INTERNET=1 python ../tests/runtests.py
```

Test data is in `tests/testdata` with format: `<pactester params> | <expected result>`. Tests use `tests/proxy.pac` as the PAC file. Set `DEBUG=1` for verbose test output.

## Architecture

### Build Flow
1. QuickJS engine compiles to `src/quickjs/libquickjs.a`
2. `pacparser.c` compiles against QuickJS headers to `pacparser.o`
3. Shared library (`libpacparser.so.1` / `.dylib` / `.dll`) links `pacparser.o` + `libquickjs.a`
4. `pactester` CLI statically links against `libpacparser.a`
5. Python C extension (`_pacparser`) wraps `pacparser.o` + `libquickjs.a` via setuptools

### Key Source Files

- **`src/pacparser.c`** — Core library. Initializes QuickJS context, evaluates PAC scripts, implements DNS helper functions (`dns_resolve`, `my_ip`). All public API functions live here.
- **`src/pacparser.h`** — Public C API (9 functions: `init`, `parse_pac_file`, `parse_pac_string`, `find_proxy`, `just_find_proxy`, `cleanup`, `setmyip`, `set_error_printer`, `version`).
- **`src/pac_utils.h`** — PAC standard JavaScript functions embedded as a C string. This is Mozilla's PAC utility implementation defining `dnsDomainIs()`, `isInNet()`, `shExpMatch()`, etc.
- **`src/pactester.c`** — CLI tool wrapping the library. Usage: `pactester -p <pacfile> -u <url> [-c client_ip] [-f urlslist]`.
- **`src/pymod/pacparser/__init__.py`** — Python API wrapper. Adds host auto-extraction from URLs and the `URLError` exception.
- **`src/pymod/pacparser_py.c`** — Python C extension binding `_pacparser` methods to the C library.
- **`src/pymod/setup.py`** — Python build config. Version derived from git tags.

### Platform Handling
The `src/Makefile` detects OS via `uname` and adjusts shared library naming, linking flags, and compiler flags for Linux, macOS, and FreeBSD. Windows uses `src/Makefile.win32` with MinGW.
1 change: 1 addition & 0 deletions sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ sonar.projectKey=manugarg_pacparser
sonar.organization=manugarg
sonar.projectName=pacparser
sonar.sources=src
sonar.exclusions=src/quickjs/**/*,src/**/quickjs.c,src/**/quickjs.h
sonar.cfamily.compile-commands=compile_commands.json
sonar.sourceEncoding=UTF-8
sonar.host.url=https://sonarcloud.io
44 changes: 16 additions & 28 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@ LIB_VER = 1
SO_SUFFIX = so
LIBRARY = $(LIBRARY_NAME).$(SO_SUFFIX).$(LIB_VER)
MKSHLIB = $(CC) -shared
LIB_OPTS = -Wl,-soname=$(LIBRARY) -Wl,-exclude-libs=libjs.a
LIB_OPTS = -Wl,-soname=$(LIBRARY) -Wl,-exclude-libs=libquickjs.a
SHFLAGS = -fPIC
SMCFLAGS = -DHAVE_VA_COPY -DVA_COPY=va_copy

ifeq ($(OS_ARCH),FreeBSD)
PREFIX ?= /usr/local
Expand All @@ -62,24 +61,17 @@ ifeq ($(OS_ARCH),Darwin)
MKSHLIB = $(CC) -dynamiclib -framework System
LIB_OPTS = -install_name $(PREFIX)/lib/$(notdir $@)
SHFLAGS =
ifeq ($(MAC_GT_OS11),false)
MAC_MINOR_VERSION := $(shell sw_vers -productVersion | cut -d. -f2)
MAC_GT_10_5 := $(shell [ $(MAC_MINOR_VERSION) -le 5 ] && echo false)
ifeq ($(MAC_GT_10_5),false)
SMCFLAGS =
endif
endif
endif

PREFIX ?= /usr
MAINT_CFLAGS := -g -DXP_UNIX -Wall -DVERSION=$(VERSION)
MAINT_CFLAGS := -g -Wall -DVERSION=$(VERSION)

ifndef PYTHON
PYTHON = python
endif

# Spidermonkey library.
MAINT_CFLAGS += -Ispidermonkey/js/src
# QuickJS library.
MAINT_CFLAGS += -Iquickjs

LIBRARY_LINK = $(LIBRARY_NAME).$(SO_SUFFIX)
PREFIX := $(DESTDIR)$(PREFIX)
Expand All @@ -92,28 +84,24 @@ MAN_PREFIX = $(PREFIX)/share/man
.PHONY: clean pymod install-pymod
all: testpactester

jsapi_buildstamp: spidermonkey/js/src
cd spidermonkey && SMCFLAGS="$(SHFLAGS) $(SMCFLAGS)" $(MAKE) jsapi
touch jsapi_buildstamp
quickjs/libquickjs.a:
cd quickjs && $(MAKE) CFLAGS="$(SHFLAGS)"

spidermonkey/libjs.a: spidermonkey/js/src
cd spidermonkey && SMCFLAGS="$(SHFLAGS) $(SMCFLAGS)" $(MAKE) jslib

pacparser.o: pacparser.c pac_utils.h pacparser.h jsapi_buildstamp
pacparser.o: pacparser.c pac_utils.h pacparser.h
$(CC) $(MAINT_CFLAGS) $(CFLAGS) $(SHFLAGS) -c pacparser.c -o pacparser.o
touch pymod/pacparser_o_buildstamp

$(LIBRARY): pacparser.o spidermonkey/libjs.a
$(MKSHLIB) $(MAINT_CFLAGS) $(CFLAGS) $(LDFLAGS) $(LIB_OPTS) -o $(LIBRARY) pacparser.o spidermonkey/libjs.a -lm
$(LIBRARY): pacparser.o quickjs/libquickjs.a
$(MKSHLIB) $(MAINT_CFLAGS) $(CFLAGS) $(LDFLAGS) $(LIB_OPTS) -o $(LIBRARY) pacparser.o quickjs/libquickjs.a -lm

libpacparser.a: pacparser.o spidermonkey/libjs.a
cp spidermonkey/libjs.a libpacparser.a
libpacparser.a: pacparser.o quickjs/libquickjs.a
cp quickjs/libquickjs.a libpacparser.a
ar rcs libpacparser.a pacparser.o

$(LIBRARY_LINK): $(LIBRARY)
ln -sf $(LIBRARY) $(LIBRARY_LINK)

pactester: pactester.c pacparser.h libpacparser.a
pactester: pactester.c pacparser.h libpacparser.a
$(CC) $(MAINT_CFLAGS) $(CFLAGS) $(LDFLAGS) pactester.c libpacparser.a -o pactester -lm -L. -I.

testpactester: pactester $(LIBRARY_LINK)
Expand Down Expand Up @@ -149,11 +137,11 @@ dist: all
zip -r $${bindir}.zip $${bindir}/.

# Targets to build python module
pymod: pacparser.o pacparser.h spidermonkey/libjs.a
pymod: pacparser.o pacparser.h quickjs/libquickjs.a
cd pymod && ARCHFLAGS="" $(PYTHON) setup.py build
$(PYTHON) ../tests/runtests.py

pymod-dist: pacparser.o pacparser.h spidermonkey/libjs.a
pymod-dist: pacparser.o pacparser.h quickjs/libquickjs.a
cd pymod && ARCHFLAGS="" $(PYTHON) setup.py build
cd pymod && ARCHFLAGS="" $(PYTHON) setup.py dist
$(PYTHON) ../tests/runtests.py
Expand All @@ -162,7 +150,7 @@ install-pymod: pymod
cd pymod && ARCHFLAGS="" $(PYTHON) setup.py install --root="$(DESTDIR)/" $(EXTRA_ARGS)

clean:
rm -f $(LIBRARY_LINK) $(LIBRARY) pacparser.o pactester pymod/pacparser_o_buildstamp jsapi_buildstamp
rm -f $(LIBRARY_LINK) $(LIBRARY) pacparser.o pactester pymod/pacparser_o_buildstamp libpacparser.a
rm -rf dist
cd pymod && $(PYTHON) setup.py clean --all
cd spidermonkey && $(MAKE) clean
cd quickjs && $(MAKE) clean
26 changes: 13 additions & 13 deletions src/Makefile.win32
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
# packaging script.
-include version.mk

ifeq ($(OS),Windows_NT)
ifeq ($(OS),Windows_NT)
RM = del /Q /F
CP = copy /Y
ifdef ComSpec
Expand All @@ -39,32 +39,32 @@ endif
VERSION ?= $(shell git describe --always --tags --candidate=100)

LIB_VER=1
CFLAGS=-g -DXP_WIN -DWINVER=0x0501 -DVERSION=$(VERSION) -Ispidermonkey/js/src -Wall
CFLAGS=-g -DWINVER=0x0501 -DVERSION=$(VERSION) -Iquickjs -Wall
CC=gcc
PYTHON ?= python

.PHONY: clean pymod install-pymod

all: pacparser.dll pactester

pacparser.o: pacparser.c pac_utils.h js.lib
pacparser.o: pacparser.c pac_utils.h quickjs/libquickjs.a
$(CC) $(CFLAGS) -c pacparser.c -o pacparser.o

js.lib:
$(MAKE) -C spidermonkey -f Makefile.win32
quickjs/libquickjs.a:
$(MAKE) -C quickjs -f Makefile.win32

pacparser.dll: pacparser.o spidermonkey/js.lib
pacparser.dll: pacparser.o quickjs/libquickjs.a
$(CC) -shared -o pacparser.dll \
-Wl,--output-def,pacparser.def \
-Wl,--out-implib,libpacparser.a \
-Wl,--export-all-symbols \
pacparser.o -ljs -Lspidermonkey -lws2_32
pacparser.o quickjs/libquickjs.a -lws2_32

pacparser.lib: pacparser.dll pacparser.def
lib /machine:i386 /def:pacparser.def

pactester: pactester.c pacparser.h pacparser.o
$(CC) pactester.c pacparser.o -o pactester -ljs -Lspidermonkey -lws2_32
pactester: pactester.c pacparser.h pacparser.o quickjs/libquickjs.a
$(CC) pactester.c pacparser.o quickjs/libquickjs.a -o pactester -lws2_32

dist: pacparser.dll pactester pacparser.def
if exist dist rmdir /s /q dist
Expand All @@ -82,15 +82,15 @@ dist: pacparser.dll pactester pacparser.def
if exist ..\docs\html xcopy ..\docs\html dist\docs

# Targets to build python module
pymod: pacparser.h pacparser.dll pacparser.o js.lib
pymod: pacparser.h pacparser.dll pacparser.o quickjs/libquickjs.a
cd pymod && $(PYTHON) setup.py build --compiler=mingw32
cd .. && $(PYTHON) tests/runtests.py

pymod-dist: pacparser.h pacparser.dll pacparser.o js.lib
pymod-dist: pacparser.h pacparser.dll pacparser.o quickjs/libquickjs.a
cd pymod && $(PYTHON) setup.py build --compiler=mingw32 && $(PYTHON) setup.py dist
cd .. && $(PYTHON) tests/runtests.py

pymod-%: pacparser.h pacparser.dll pacparser.o js.lib
pymod-%: pacparser.h pacparser.dll pacparser.o quickjs/libquickjs.a
cd pymod && py -$* setup.py build --compiler=mingw32
cd .. && py -$* tests\runtests.py

Expand All @@ -99,6 +99,6 @@ pymod-dist-%:

clean:
$(RM) pacparser.dll *.lib pacparser.def pacparser.exp pacparser.o pactester.exe libpacparser.a
$(MAKE) -C spidermonkey -f Makefile.win32 clean
$(MAKE) -C quickjs -f Makefile.win32 clean
cd pymod && $(PYTHON) setup.py clean --all
$(RM) dist
2 changes: 1 addition & 1 deletion src/pac_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ static const char *pacUtils =
"}\n"

"function isInNet(ipaddr, pattern, maskstr) {\n"
" var test = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/(ipaddr);\n"
" var test = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(ipaddr);\n"
" if (test == null) {\n"
" ipaddr = dnsResolve(ipaddr);\n"
" if (ipaddr == null)\n"
Expand Down
Loading
Loading