# libexpat: Streaming XML parsing in C99

> libexpat is a C99 library that parses XML incrementally through registered callbacks. It suits native C and C++ applications that need to process XML without first loading an entire document, but its warning about unfixed security issues is a serious adoption constraint.

**libexpat/libexpat** — :herb: Fast streaming XML parser written in C99 with >90% test coverage; moved from SourceForge to GitHub

- Repository: https://github.com/libexpat/libexpat
- Website: https://libexpat.github.io/
- Stars: 1,396 · Forks: 559
- Language: C
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/libexpat-libexpat

## What libexpat parses and who should use it

libexpat addresses the need to process XML 1.0 Fourth Edition documents inside applications written in C or C++. It is a library rather than a command-line XML converter. A C program links against Expat, registers callbacks, and feeds document bytes to a parser instance. This design is relevant to programs that handle network responses, configuration files, document fragments or other XML sources whose complete contents are not required at once.

The project is aimed primarily at developers who need parser behavior under their own control. The listed compiler targets include GNU GCC 4.5 or newer for C, GNU GCC 4.8.1 or newer for C++, LLVM Clang 3.5 or newer, and Microsoft Visual Studio 2022. Windows developers can also obtain release archives containing precompiled libraries, executables and source.

This is not a general-purpose XML data-binding library. The README does not describe mapping XML into language objects, maintaining an in-memory tree, editing documents, validating DTD content or applying a query language. Those tasks may require additional application code or a different library. Expat's narrower role is lexical parsing and event delivery.

The repository is not archived, and its last push was on 2026-10-15. Release 2.9.0 was published on 2026-10-05, following 2.8.5 on 2026-09-22 and 2.8.4 on 2026-08-31. This establishes recent maintenance activity, but repository recency does not erase the README warning that Expat has unfixed security issues. Any deployment processing hostile input must examine that warning as part of version selection.

## Expat's callback-driven XML data flow

Expat is stream-oriented. A program registers handlers before parsing begins, then the parser calls the relevant handler when it encounters the corresponding XML structure. The README uses a start tag as the example structure. This callback model avoids requiring the parser to expose a completed document tree before the application can begin handling its contents.

Conceptually, input moves from the application into an Expat parser instance, the parser recognizes XML events, and the registered C function receives the event. The application can inspect the relevant data and return control to parsing. The repository topics include streaming parser and XML parsing, while the README describes handlers registered before parsing. The README does not include a complete C event-handler example, buffer-management example or error-handling routine, so those details cannot be inferred from this overview.

That omission also defines Expat's boundary. It provides parsing primitives and notification, while the consuming application owns policy decisions. For example, nothing in the README says that Expat automatically limits business-level payload sizes, rejects a particular application schema or changes behavior according to an external policy. XML parsing validity and application-level input safety are related but not interchangeable concerns.

The parser targets XML 1.0 Fourth Edition. Applications that require XML schema validation, canonicalization or another specialized processing capability need to verify whether Expat or a companion component covers that requirement. The README does not document any such guarantee.

## Building libexpat from a Git checkout

Building directly from the Git repository first requires the generation of an Autoconf configure script. The project requires autoconf 2.58 or newer for this preparation step and uses libtool as part of the source-build toolchain. Run the repository's build configuration script from the checkout root. It generates the configure script used by the subsequent source-distribution workflow.

Use this command:

```bash
./buildconf.sh
```

A successful run leaves the checkout prepared for the documented Autotools sequence. This operation generates build files, so it modifies the local source tree. It is separate from compiling Expat and should not be confused with installation into the application.

The `expat/` directory is the library area used by the documented CMake configuration, while the repository root also contains CI files, licensing material, contribution guidance and test data. Because Expat can be consumed as a system package, a source build is not mandatory for every application. Developers may instead link an installed copy through CMake. The choice between an installed package and a source-fetched dependency affects update work, binary provenance and build reproducibility.

## Installing Expat with CMake or Autotools

CMake projects have three documented integration paths. Module mode uses CMake's `FindEXPAT` and the uppercase imported target `EXPAT::EXPAT`. Config mode requires package files supplied by the packaging or build system, uses lowercase `expat` in the package call, and links `expat::expat`. Its documented minimum Expat version depends on how Expat was packaged: 2.2.8 for CMake-built packages, 2.3.0 for Autotools packages on Linux, or 2.4.0 for Autotools packages on macOS or MinGW.

The `FetchContent` path requires CMake 3.18 or newer. Its README example declares the Git repository, sets `GIT_TAG` to a release tag of the form `R_X_Y_Z`, sets `SOURCE_SUBDIR` to `expat/`, and links the `expat` target. The documentation advises against using `master` or a non-release SHA1. A fixed commit supports repeatable builds, but the dependency reference must also be updated when a newer release carrying security fixes is needed.

For a source distribution, configure the build first:

```bash
./configure
```

The default installation prefix shown by the README is `/usr/local`, with the library under `/usr/local/lib`, the header under `/usr/local/include`, and `xmlwf` under `/usr/local/bin`. A private prefix can be selected explicitly:

```bash
./configure --prefix=/home/me/mystuff
```

After configuration, compile and install the result:

```bash
make
make install
```

The process requires write access to the selected installation directories. The README says additional `make` options can be found in the generated `Makefile`.

## A first parser integration and its missing examples

The clearest first use documented by the README is linking a small CMake application to an installed Expat package. Module mode requires `find_package(EXPAT 2.2.8 MODULE REQUIRED)`, followed by linking the executable to `EXPAT::EXPAT`. The application source named by that example is `hello.c`.

However, the README does not include the contents of `hello.c`. It does not show an XML input string, parser initialization, byte-feed call, callback registration, finalization call or error check. Supplying such an example would require inventing API names and behavior that the README does not state. For that reason, a complete first parse cannot be reproduced from the excerpt alone.

A real migration should start from an existing C or C++ XML parsing path and introduce the Expat calls through the project's official API documentation. The immediate integration checkpoint is that CMake finds the intended Expat version and resolves the correct imported target. A faulty choice between Module and Config mode can prevent configuration even when a usable Expat installation exists.

The README also mentions using `XML_LARGE_SIZE` to enable 64-bit integers for line numbers, column numbers and the overall byte index. It explicitly describes this as an ABI-breaking change and says it is not recommended for general use. Applications should leave that option out unless they control both the library and consumer builds and have a specific requirement.

## Security limits in the current Expat release

The most significant limitation is stated directly at the top of the README: Expat has unfixed security issues. The README directs readers to GitHub issue 1160 for details. Because the issue list is not reproduced in the README, this review cannot name affected inputs, impact conditions or safe versions. Release 2.9.0 should not be presented as resolving the warning.

This restriction is especially significant for XML exposed to networks, uploaded files or other less trusted sources. Expat's streaming design does not itself guarantee that processing hostile input is free of denial-of-service conditions, malformed-input defects or other security concerns. The correct boundary is narrower: Expat handles parsing, but the consumer must establish whether the current version is suitable for its threat conditions.

Applications with a packaged Expat also have a dependency update path outside this repository. A Linux distribution, container base image or build environment may provide its own version and patch set. A libexpat release cannot prove which revision is installed in another package. An adopter needs to inspect the actual linked binary and resolve issue 1160 before relying on a claim of safety.

The warning also affects upgrade policy. A deliberately pinned `FetchContent` commit may preserve reproducibility while delaying a necessary security update. That is a direct trade-off between dependency stability and timely patching, not something solved by leaving the Git reference unchanged.

## libexpat compared with a document-tree XML library

The practical alternative is a document-tree XML library. Such a library generally builds a representation of the parsed document and lets application code traverse elements, attributes and text after parsing. Expat instead reports structures through callbacks while input is being processed. These approaches differ in data ownership and application architecture.

A tree-based approach is easier when application logic naturally performs several random-access queries over a small document. It also moves much of the data-model representation into the dependency. Expat suits code that wants to handle a start tag as soon as it is encountered, reject or ignore content according to local rules, or avoid keeping a complete parsed document in memory.

Expat is the wrong fit when the requirement is explicitly to retrieve arbitrary descendants later, edit XML, serialize a modified tree or rely on a higher-level data-binding system. Those capabilities are not documented in the README. A different library with a tree or object model would match that architecture more directly.

The callback model also shifts work to the integrating project. Since the README does not show error-handling and parser-lifecycle code, developers need additional official guidance before judging how much application code a full integration requires. That uncertainty is more relevant for a new dependency than a repository's stated test coverage.

## Maintenance, licensing and upgrade cost

Maintenance is current based on repository facts: the project is not archived, its last push was on 2026-10-15, and releases appeared on 2026-08-31, 2026-09-22 and 2026-10-05. That timeline indicates continued release work. It does not mean every deployment receives fixes automatically.

Projects using Module mode or Config mode need to track the Expat version supplied by their operating system or environment. A source build adds the burden of generating build files, configuring, compiling and installing. A `FetchContent` integration adds a Git dependency and a pinned revision that must be reviewed for each release. The README explicitly requires manual or automated tag updates so security fixes are not missed.

libexpat uses the MIT/X Consortium license. The README states that users may copy, distribute and modify it under the terms in the `COPYING` file. This identifies the offered permissions and conditions, but this review does not interpret how the license interacts with a particular product distribution. Projects with licensing requirements should inspect the license text and their dependency notices.

## Conclusion

libexpat remains appropriate for native C and C++ software that benefits from callback-driven XML processing, especially where an existing XML library dependency is already being updated. Applications handling untrusted XML should not adopt the current release until they have reviewed issue 1160 and determined whether an official fixed release is available. Maintainers embedding Expat should verify the exact version, selected CMake integration, ABI requirements and security notice before merging it.

## FAQ

### How do I install libexpat on a system from a Git checkout?

Run `./buildconf.sh` first to generate the Autotools configure script. Then run `./configure`, `make`, and `make install`, selecting a private installation location with `--prefix` if required.

### How does a CMake project link to libexpat?

The README documents Module mode through `EXPAT::EXPAT`, Config mode through `expat::expat`, and `FetchContent` through the `expat` target. The requirements differ, so the project layout and Expat package version determine which mode applies.

### Is libexpat currently safe to use for untrusted XML?

The README says Expat has unfixed security issues and links to GitHub issue 1160 for details. Review that issue and the actual deployed version before using the current release for untrusted input.

## Sources

- [libexpat/libexpat on GitHub](https://github.com/libexpat/libexpat)
- [License: MIT](https://github.com/libexpat/libexpat/blob/master/LICENSE)
- [Project website](https://libexpat.github.io/)
- [README](https://github.com/libexpat/libexpat/blob/master/README.md)
- [Releases](https://github.com/libexpat/libexpat/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/libexpat-libexpat
