Fun API Documentation 0.42.1
The programming language that makes you have fun!
Loading...
Searching...
No Matches
pcsc.c
Go to the documentation of this file.
1/*
2 * This file is part of the Fun programming language.
3 * https://fun-lang.xyz/
4 *
5 * Copyright 2025 Johannes Findeisen <you@hanez.org>
6 * Licensed under the terms of the Apache-2.0 license.
7 * https://opensource.org/license/apache-2-0
8 */
9
10/**
11 * @file pcsc.c
12 * @brief PC/SC smartcard helper registries and lookup utilities for VM opcodes.
13 *
14 * This module centralizes tiny fixed-size registries for PC/SC resources and
15 * minimal helper functions used by VM opcodes under src/vm/pcsc/*.c. Keeping
16 * the concrete handle management here allows the opcode implementations to
17 * focus on VM stack marshalling, mirroring the approach taken by other
18 * extensions (PCRE2, SQLite, XML2, JSON, INI, cURL, OpenSSL).
19 *
20 * Build-time feature flag:
21 * - All code in this file is compiled only when FUN_WITH_PCSC is enabled. When
22 * disabled, the referencing opcodes should compile to safe no-op fallbacks
23 * (typically pushing 0, Nil, or empty arrays) while retaining the same stack
24 * behaviour.
25 *
26 * Registries and ownership model:
27 * - Context registry (g_pcsc_ctx): stores SCARDCONTEXT values obtained via
28 * SCardEstablishContext(). The registry DOES NOT call SCardReleaseContext()
29 * automatically — releasing is the responsibility of the opcode that owns
30 * the lifecycle. The registry merely tracks usage and provides a stable,
31 * small integer id for referencing a context in later operations.
32 * - Card registry (g_pcsc_card): stores SCARDHANDLE and the negotiated
33 * protocol (proto) obtained via SCardConnect(). Similarly, the registry does
34 * not call SCardDisconnect(); the VM opcode that created the handle is in
35 * charge of eventual teardown.
36 * - Handles are 1-based indices into the fixed arrays (contexts: up to 8,
37 * cards: up to 32). A return value of 0 indicates failure (no free slot or
38 * invalid request).
39 *
40 * Error handling and limits:
41 * - Allocation helpers return 0 when no free slot is available. Lookup helpers
42 * return NULL if the id is out of range or the slot is not currently in use.
43 * - The fixed sizes (8 contexts, 32 cards) are pragmatic defaults for typical
44 * scripts. Increase cautiously if your workloads require more concurrent
45 * resources.
46 *
47 * Thread-safety:
48 * - This module is not thread-safe. If the interpreter is used from multiple
49 * threads, coordinate access to these registries externally.
50 */
51
52#ifdef FUN_WITH_PCSC
53#if defined(__has_include)
54#if __has_include(<PCSC/winscard.h>)
55#include <PCSC/winscard.h>
56#include <PCSC/wintypes.h>
57#elif __has_include(<winscard.h>)
58#include <winscard.h>
59#else
60#error "FUN_WITH_PCSC is enabled but PCSC headers were not found"
61#endif
62#else
63#include <PCSC/winscard.h>
64#include <PCSC/wintypes.h>
65#endif
66#include <string.h>
67#include <stdio.h>
68
69/**
70 * @brief One slot in the PC/SC context registry.
71 *
72 * Ownership notes:
73 * - The registry does not own the context; it merely stores the value returned
74 * by SCardEstablishContext(). Callers/opcodes are responsible for invoking
75 * SCardReleaseContext() at the appropriate time.
76 */
77typedef struct {
78 SCARDCONTEXT ctx; /**< Established context value. */
79 int in_use; /**< 1 if the slot is currently allocated; 0 otherwise. */
81
82/**
83 * @brief One slot in the PC/SC card handle registry.
84 *
85 * Ownership notes:
86 * - The registry does not disconnect cards; it only stores the handle returned
87 * by SCardConnect() and the negotiated protocol. The VM opcode that created
88 * the handle is responsible for calling SCardDisconnect().
89 */
90typedef struct {
91 SCARDHANDLE h; /**< Connected card handle from SCardConnect(). */
92 DWORD proto; /**< Negotiated protocol flags (e.g., SCARD_PROTOCOL_T0/T1). */
93 int in_use; /**< 1 if the slot is currently allocated; 0 otherwise. */
95
98
99/**
100 * @brief Allocate a free context slot in the PC/SC registry.
101 *
102 * Scans the small fixed-size context registry for an available slot, marks it
103 * as in use, clears the stored value, and returns a 1-based identifier.
104 *
105 * @return int A 1-based slot id on success; 0 if no free slot is available.
106 */
107static int pcsc_alloc_ctx_slot(void) {
108 for (int i = 0; i < (int)(sizeof(g_pcsc_ctx) / sizeof(g_pcsc_ctx[0])); ++i) {
109 if (!g_pcsc_ctx[i].in_use) {
110 g_pcsc_ctx[i].in_use = 1;
111 g_pcsc_ctx[i].ctx = 0;
112 return i + 1;
113 }
114 }
115 return 0;
116}
117
118/**
119 * @brief Allocate a free card slot in the PC/SC registry.
120 *
121 * Scans the card registry for an unused entry, sets initial values, and
122 * returns a small positive identifier that can be used by opcodes to index the
123 * slot later.
124 *
125 * @return int A 1-based slot id on success; 0 if no free slot is available.
126 */
127static int pcsc_alloc_card_slot(void) {
128 for (int i = 0; i < (int)(sizeof(g_pcsc_card) / sizeof(g_pcsc_card[0])); ++i) {
129 if (!g_pcsc_card[i].in_use) {
130 g_pcsc_card[i].in_use = 1;
131 g_pcsc_card[i].h = 0;
132 g_pcsc_card[i].proto = 0;
133 return i + 1;
134 }
135 }
136 return 0;
137}
138
139/**
140 * @brief Lookup a context slot by id.
141 *
142 * Validates the provided 1-based identifier, ensures the slot is currently in
143 * use, and returns a pointer to the internal registry entry.
144 *
145 * @param id int 1-based context id previously returned by pcsc_alloc_ctx_slot().
146 * @return pcsc_ctx_entry* Pointer to the entry if valid and in use; NULL otherwise.
147 */
149 if (id <= 0) return 0;
150 int idx = id - 1;
151 if (idx < 0 || idx >= (int)(sizeof(g_pcsc_ctx) / sizeof(g_pcsc_ctx[0]))) return 0;
152 if (!g_pcsc_ctx[idx].in_use) return 0;
153 return &g_pcsc_ctx[idx];
154}
155
156/**
157 * @brief Lookup a card slot by id.
158 *
159 * Validates the provided 1-based identifier, ensures the slot is currently in
160 * use, and returns a pointer to the internal registry entry, which exposes the
161 * SCARDHANDLE and negotiated protocol for subsequent operations (e.g.,
162 * SCardTransmit()).
163 *
164 * @param id int 1-based card id previously returned by pcsc_alloc_card_slot().
165 * @return pcsc_card_entry* Pointer to the entry if valid and in use; NULL otherwise.
166 */
168 if (id <= 0) return 0;
169 int idx = id - 1;
170 if (idx < 0 || idx >= (int)(sizeof(g_pcsc_card) / sizeof(g_pcsc_card[0]))) return 0;
171 if (!g_pcsc_card[idx].in_use) return 0;
172 return &g_pcsc_card[idx];
173}
174#endif
static pcsc_card_entry g_pcsc_card[32]
Definition pcsc.c:97
static pcsc_card_entry * pcsc_get_card(int id)
Lookup a card slot by id.
Definition pcsc.c:167
static int pcsc_alloc_ctx_slot(void)
Allocate a free context slot in the PC/SC registry.
Definition pcsc.c:107
static pcsc_ctx_entry g_pcsc_ctx[8]
Definition pcsc.c:96
static int pcsc_alloc_card_slot(void)
Allocate a free card slot in the PC/SC registry.
Definition pcsc.c:127
static pcsc_ctx_entry * pcsc_get_ctx(int id)
Lookup a context slot by id.
Definition pcsc.c:148
One slot in the PC/SC card handle registry.
Definition pcsc.c:90
DWORD proto
Definition pcsc.c:92
SCARDHANDLE h
Definition pcsc.c:91
int in_use
Definition pcsc.c:93
One slot in the PC/SC context registry.
Definition pcsc.c:77
SCARDCONTEXT ctx
Definition pcsc.c:78
int in_use
Definition pcsc.c:79