A stack which works with or without an Mbed os library. Provides IPv4 or IPv6 with a full 1500 byte buffer.
Dependents: oldheating gps motorhome heating
resolve/ar4.c@195:bd5b123143ca, 2021-04-18 (annotated)
- Committer:
- andrewboyson
- Date:
- Sun Apr 18 19:04:48 2021 +0000
- Revision:
- 195:bd5b123143ca
- Parent:
- 193:47a953ab571b
- Child:
- 200:5acbc41bf469
Added a user module to allow user defined UDPs port to be used - in this case for a WIZ module.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
andrewboyson | 61:aad055f1b0d1 | 1 | #include <stdint.h> |
andrewboyson | 61:aad055f1b0d1 | 2 | #include <stdbool.h> |
andrewboyson | 60:1d8c7a1e7483 | 3 | #include "log.h" |
andrewboyson | 60:1d8c7a1e7483 | 4 | #include "net.h" |
andrewboyson | 60:1d8c7a1e7483 | 5 | #include "mac.h" |
andrewboyson | 60:1d8c7a1e7483 | 6 | #include "ip4addr.h" |
andrewboyson | 60:1d8c7a1e7483 | 7 | #include "arp.h" |
andrewboyson | 60:1d8c7a1e7483 | 8 | #include "http.h" |
andrewboyson | 93:580fc113d9e9 | 9 | #include "mstimer.h" |
andrewboyson | 60:1d8c7a1e7483 | 10 | |
andrewboyson | 60:1d8c7a1e7483 | 11 | bool Ar4Trace = false; |
andrewboyson | 60:1d8c7a1e7483 | 12 | |
andrewboyson | 93:580fc113d9e9 | 13 | #define CACHE_TIMEOUT_MS 3600 * 1000 |
andrewboyson | 93:580fc113d9e9 | 14 | #define FREEZE_TIMEOUT_MS 1800 * 1000 |
andrewboyson | 116:60521b29e4c9 | 15 | #define REPLY_TIMEOUT_MS 1 * 1000 |
andrewboyson | 60:1d8c7a1e7483 | 16 | #define SEND_ATTEMPTS 3 |
andrewboyson | 60:1d8c7a1e7483 | 17 | #define RECORDS_COUNT 20 |
andrewboyson | 60:1d8c7a1e7483 | 18 | |
andrewboyson | 60:1d8c7a1e7483 | 19 | #define STATE_EMPTY 0 |
andrewboyson | 60:1d8c7a1e7483 | 20 | #define STATE_WANT 1 |
andrewboyson | 60:1d8c7a1e7483 | 21 | #define STATE_SENT 2 |
andrewboyson | 60:1d8c7a1e7483 | 22 | #define STATE_VALID 3 |
andrewboyson | 60:1d8c7a1e7483 | 23 | |
andrewboyson | 60:1d8c7a1e7483 | 24 | struct record |
andrewboyson | 60:1d8c7a1e7483 | 25 | { |
andrewboyson | 60:1d8c7a1e7483 | 26 | uint32_t elapsed; |
andrewboyson | 60:1d8c7a1e7483 | 27 | uint32_t ip; |
andrewboyson | 60:1d8c7a1e7483 | 28 | uint8_t state; |
andrewboyson | 60:1d8c7a1e7483 | 29 | uint8_t tries; |
andrewboyson | 60:1d8c7a1e7483 | 30 | char mac[6]; |
andrewboyson | 60:1d8c7a1e7483 | 31 | }; |
andrewboyson | 60:1d8c7a1e7483 | 32 | static struct record records[RECORDS_COUNT]; |
andrewboyson | 60:1d8c7a1e7483 | 33 | static int getExistingIp(uint32_t ip) |
andrewboyson | 60:1d8c7a1e7483 | 34 | { |
andrewboyson | 60:1d8c7a1e7483 | 35 | for (int i = 0; i < RECORDS_COUNT; i++) |
andrewboyson | 60:1d8c7a1e7483 | 36 | { |
andrewboyson | 60:1d8c7a1e7483 | 37 | if (records[i].state && records[i].ip == ip) return i; |
andrewboyson | 60:1d8c7a1e7483 | 38 | } |
andrewboyson | 60:1d8c7a1e7483 | 39 | return -1; |
andrewboyson | 60:1d8c7a1e7483 | 40 | } |
andrewboyson | 60:1d8c7a1e7483 | 41 | static int getOldest() |
andrewboyson | 60:1d8c7a1e7483 | 42 | { |
andrewboyson | 93:580fc113d9e9 | 43 | int iOldest = 0; |
andrewboyson | 93:580fc113d9e9 | 44 | uint32_t ageOldest = 0; |
andrewboyson | 60:1d8c7a1e7483 | 45 | for (int i = 0; i < RECORDS_COUNT; i++) |
andrewboyson | 60:1d8c7a1e7483 | 46 | { |
andrewboyson | 60:1d8c7a1e7483 | 47 | if (!records[i].state) return i; //Found an empty slot so just return it |
andrewboyson | 93:580fc113d9e9 | 48 | uint32_t age = MsTimerCount - records[i].elapsed; |
andrewboyson | 93:580fc113d9e9 | 49 | if (age >= ageOldest) |
andrewboyson | 60:1d8c7a1e7483 | 50 | { |
andrewboyson | 93:580fc113d9e9 | 51 | ageOldest = age; |
andrewboyson | 93:580fc113d9e9 | 52 | iOldest = i; |
andrewboyson | 60:1d8c7a1e7483 | 53 | } |
andrewboyson | 60:1d8c7a1e7483 | 54 | } |
andrewboyson | 93:580fc113d9e9 | 55 | return iOldest; //Otherwise return the oldest |
andrewboyson | 60:1d8c7a1e7483 | 56 | } |
andrewboyson | 60:1d8c7a1e7483 | 57 | void Ar4MakeRequestForMacFromIp(uint32_t ip) |
andrewboyson | 60:1d8c7a1e7483 | 58 | { |
andrewboyson | 60:1d8c7a1e7483 | 59 | //Don't treat non ips |
andrewboyson | 60:1d8c7a1e7483 | 60 | if (!ip) return; |
andrewboyson | 60:1d8c7a1e7483 | 61 | int i; |
andrewboyson | 60:1d8c7a1e7483 | 62 | |
andrewboyson | 60:1d8c7a1e7483 | 63 | //If a record already exists then request an update |
andrewboyson | 60:1d8c7a1e7483 | 64 | i = getExistingIp(ip); |
andrewboyson | 60:1d8c7a1e7483 | 65 | if (i > -1) |
andrewboyson | 60:1d8c7a1e7483 | 66 | { |
andrewboyson | 133:a37eb35a03f1 | 67 | if (!MsTimerRelative(records[i].elapsed, FREEZE_TIMEOUT_MS)) return; |
andrewboyson | 60:1d8c7a1e7483 | 68 | if (Ar4Trace) |
andrewboyson | 60:1d8c7a1e7483 | 69 | { |
andrewboyson | 60:1d8c7a1e7483 | 70 | LogTimeF("AR4 Updated request for MAC of "); |
andrewboyson | 187:122fc1996c86 | 71 | Ip4AddrLog(ip); |
andrewboyson | 60:1d8c7a1e7483 | 72 | Log("\r\n"); |
andrewboyson | 60:1d8c7a1e7483 | 73 | } |
andrewboyson | 60:1d8c7a1e7483 | 74 | records[i].state = STATE_WANT; |
andrewboyson | 60:1d8c7a1e7483 | 75 | records[i].tries = 0; |
andrewboyson | 93:580fc113d9e9 | 76 | records[i].elapsed = MsTimerCount; |
andrewboyson | 60:1d8c7a1e7483 | 77 | return; |
andrewboyson | 60:1d8c7a1e7483 | 78 | } |
andrewboyson | 60:1d8c7a1e7483 | 79 | |
andrewboyson | 60:1d8c7a1e7483 | 80 | //If a record does not exist then find the first empty slot and add the IP and date |
andrewboyson | 60:1d8c7a1e7483 | 81 | if (Ar4Trace) |
andrewboyson | 60:1d8c7a1e7483 | 82 | { |
andrewboyson | 60:1d8c7a1e7483 | 83 | LogTimeF("AR4 Made request for MAC of "); |
andrewboyson | 187:122fc1996c86 | 84 | Ip4AddrLog(ip); |
andrewboyson | 60:1d8c7a1e7483 | 85 | Log("\r\n"); |
andrewboyson | 60:1d8c7a1e7483 | 86 | } |
andrewboyson | 60:1d8c7a1e7483 | 87 | i = getOldest(); |
andrewboyson | 60:1d8c7a1e7483 | 88 | records[i].ip = ip; |
andrewboyson | 60:1d8c7a1e7483 | 89 | records[i].state = STATE_WANT; |
andrewboyson | 60:1d8c7a1e7483 | 90 | records[i].tries = 0; |
andrewboyson | 93:580fc113d9e9 | 91 | records[i].elapsed = MsTimerCount; |
andrewboyson | 60:1d8c7a1e7483 | 92 | MacClear(records[i].mac); |
andrewboyson | 60:1d8c7a1e7483 | 93 | } |
andrewboyson | 74:c3756bfa960e | 94 | int Ar4AddIpRecord(void (*traceback)(void), char* mac, uint32_t ip) |
andrewboyson | 60:1d8c7a1e7483 | 95 | { |
andrewboyson | 60:1d8c7a1e7483 | 96 | //Don't treat non ips |
andrewboyson | 60:1d8c7a1e7483 | 97 | if (!ip) |
andrewboyson | 60:1d8c7a1e7483 | 98 | { |
andrewboyson | 60:1d8c7a1e7483 | 99 | if (Ar4Trace) |
andrewboyson | 60:1d8c7a1e7483 | 100 | { |
andrewboyson | 60:1d8c7a1e7483 | 101 | LogTime("Ar4AddIpRecord had blank ip\r\n"); |
andrewboyson | 60:1d8c7a1e7483 | 102 | if (NetTraceStack) traceback(); |
andrewboyson | 60:1d8c7a1e7483 | 103 | } |
andrewboyson | 74:c3756bfa960e | 104 | return -1; |
andrewboyson | 60:1d8c7a1e7483 | 105 | } |
andrewboyson | 60:1d8c7a1e7483 | 106 | if (MacIsEmpty(mac)) |
andrewboyson | 60:1d8c7a1e7483 | 107 | { |
andrewboyson | 60:1d8c7a1e7483 | 108 | if (Ar4Trace) |
andrewboyson | 60:1d8c7a1e7483 | 109 | { |
andrewboyson | 60:1d8c7a1e7483 | 110 | LogTime("Ar4AddIpRecord had blank mac\r\n"); |
andrewboyson | 60:1d8c7a1e7483 | 111 | if (NetTraceStack) traceback(); |
andrewboyson | 60:1d8c7a1e7483 | 112 | } |
andrewboyson | 74:c3756bfa960e | 113 | return -1; |
andrewboyson | 60:1d8c7a1e7483 | 114 | } |
andrewboyson | 60:1d8c7a1e7483 | 115 | int i; |
andrewboyson | 60:1d8c7a1e7483 | 116 | |
andrewboyson | 60:1d8c7a1e7483 | 117 | //See if any record corresponds to the IP and, if so, update the MAC and time |
andrewboyson | 60:1d8c7a1e7483 | 118 | i = getExistingIp(ip); |
andrewboyson | 60:1d8c7a1e7483 | 119 | if (i > -1) |
andrewboyson | 60:1d8c7a1e7483 | 120 | { |
andrewboyson | 93:580fc113d9e9 | 121 | records[i].elapsed = MsTimerCount; |
andrewboyson | 93:580fc113d9e9 | 122 | records[i].state = STATE_VALID; |
andrewboyson | 60:1d8c7a1e7483 | 123 | MacCopy(records[i].mac, mac); |
andrewboyson | 74:c3756bfa960e | 124 | return i; |
andrewboyson | 60:1d8c7a1e7483 | 125 | } |
andrewboyson | 60:1d8c7a1e7483 | 126 | |
andrewboyson | 60:1d8c7a1e7483 | 127 | //Otherwise find the first empty slot and add the IP, MAC, and date |
andrewboyson | 60:1d8c7a1e7483 | 128 | i = getOldest(); |
andrewboyson | 60:1d8c7a1e7483 | 129 | records[i].ip = ip; |
andrewboyson | 93:580fc113d9e9 | 130 | records[i].elapsed = MsTimerCount; |
andrewboyson | 60:1d8c7a1e7483 | 131 | records[i].state = STATE_VALID; |
andrewboyson | 60:1d8c7a1e7483 | 132 | MacCopy(records[i].mac, mac); |
andrewboyson | 74:c3756bfa960e | 133 | return i; |
andrewboyson | 60:1d8c7a1e7483 | 134 | } |
andrewboyson | 60:1d8c7a1e7483 | 135 | void Ar4IpToMac(uint32_t ip, char* mac) |
andrewboyson | 60:1d8c7a1e7483 | 136 | { |
andrewboyson | 60:1d8c7a1e7483 | 137 | for (int i = 0; i < RECORDS_COUNT; i++) |
andrewboyson | 60:1d8c7a1e7483 | 138 | { |
andrewboyson | 60:1d8c7a1e7483 | 139 | if (records[i].state == STATE_VALID && records[i].ip == ip) |
andrewboyson | 60:1d8c7a1e7483 | 140 | { |
andrewboyson | 60:1d8c7a1e7483 | 141 | MacCopy(mac, records[i].mac); |
andrewboyson | 60:1d8c7a1e7483 | 142 | return; |
andrewboyson | 60:1d8c7a1e7483 | 143 | } |
andrewboyson | 60:1d8c7a1e7483 | 144 | } |
andrewboyson | 60:1d8c7a1e7483 | 145 | MacClear(mac); |
andrewboyson | 60:1d8c7a1e7483 | 146 | } |
andrewboyson | 195:bd5b123143ca | 147 | uint32_t Ar4GetIpFromMac(char* pMac) |
andrewboyson | 195:bd5b123143ca | 148 | { |
andrewboyson | 195:bd5b123143ca | 149 | for (int i = 0; i < RECORDS_COUNT; i++) |
andrewboyson | 195:bd5b123143ca | 150 | { |
andrewboyson | 195:bd5b123143ca | 151 | if (records[i].state == STATE_VALID && MacIsSame(records[i].mac, pMac)) return records[i].ip; |
andrewboyson | 195:bd5b123143ca | 152 | } |
andrewboyson | 195:bd5b123143ca | 153 | return 0; |
andrewboyson | 195:bd5b123143ca | 154 | } |
andrewboyson | 116:60521b29e4c9 | 155 | bool Ar4HaveMacForIp(uint32_t ip) |
andrewboyson | 116:60521b29e4c9 | 156 | { |
andrewboyson | 116:60521b29e4c9 | 157 | for (int i = 0; i < RECORDS_COUNT; i++) |
andrewboyson | 116:60521b29e4c9 | 158 | { |
andrewboyson | 116:60521b29e4c9 | 159 | if (records[i].state == STATE_VALID && records[i].ip == ip) return true; |
andrewboyson | 116:60521b29e4c9 | 160 | } |
andrewboyson | 116:60521b29e4c9 | 161 | return false; |
andrewboyson | 116:60521b29e4c9 | 162 | } |
andrewboyson | 193:47a953ab571b | 163 | bool Ar4CheckHaveMacAndFetchIfNot(uint32_t ip) |
andrewboyson | 193:47a953ab571b | 164 | { |
andrewboyson | 193:47a953ab571b | 165 | if (!Ar4HaveMacForIp(ip)) |
andrewboyson | 193:47a953ab571b | 166 | { |
andrewboyson | 193:47a953ab571b | 167 | Ar4MakeRequestForMacFromIp(ip); //The request is only repeated if made after a freeze time - call as often as you want. |
andrewboyson | 193:47a953ab571b | 168 | return false; |
andrewboyson | 193:47a953ab571b | 169 | } |
andrewboyson | 193:47a953ab571b | 170 | return true; |
andrewboyson | 193:47a953ab571b | 171 | } |
andrewboyson | 74:c3756bfa960e | 172 | uint32_t Ar4IndexToIp(int i) |
andrewboyson | 74:c3756bfa960e | 173 | { |
andrewboyson | 74:c3756bfa960e | 174 | return records[i].ip; |
andrewboyson | 74:c3756bfa960e | 175 | } |
andrewboyson | 60:1d8c7a1e7483 | 176 | void Ar4SendHttp() |
andrewboyson | 60:1d8c7a1e7483 | 177 | { |
andrewboyson | 60:1d8c7a1e7483 | 178 | for (int i = 0; i < RECORDS_COUNT; i++) |
andrewboyson | 60:1d8c7a1e7483 | 179 | { |
andrewboyson | 60:1d8c7a1e7483 | 180 | if (records[i].state) |
andrewboyson | 60:1d8c7a1e7483 | 181 | { |
andrewboyson | 93:580fc113d9e9 | 182 | HttpAddF("%4u ", (MsTimerCount - records[i].elapsed) / 1000 / 60); |
andrewboyson | 60:1d8c7a1e7483 | 183 | |
andrewboyson | 187:122fc1996c86 | 184 | int ipLen = Ip4AddrHttp(records[i].ip); |
andrewboyson | 159:3ebef2d02f7f | 185 | HttpAddFillChar(' ', 40 - ipLen); |
andrewboyson | 60:1d8c7a1e7483 | 186 | |
andrewboyson | 60:1d8c7a1e7483 | 187 | MacHttp(records[i].mac); |
andrewboyson | 60:1d8c7a1e7483 | 188 | |
andrewboyson | 60:1d8c7a1e7483 | 189 | HttpAddChar('\r'); |
andrewboyson | 60:1d8c7a1e7483 | 190 | HttpAddChar('\n'); |
andrewboyson | 60:1d8c7a1e7483 | 191 | } |
andrewboyson | 60:1d8c7a1e7483 | 192 | } |
andrewboyson | 60:1d8c7a1e7483 | 193 | } |
andrewboyson | 140:9000ea70b220 | 194 | void Ar4SendAjax() |
andrewboyson | 140:9000ea70b220 | 195 | { |
andrewboyson | 140:9000ea70b220 | 196 | for (int i = 0; i < RECORDS_COUNT; i++) |
andrewboyson | 140:9000ea70b220 | 197 | { |
andrewboyson | 140:9000ea70b220 | 198 | if (records[i].state) |
andrewboyson | 140:9000ea70b220 | 199 | { |
andrewboyson | 167:3ba4e3c49631 | 200 | HttpAddByteAsHex(i); |
andrewboyson | 167:3ba4e3c49631 | 201 | HttpAddChar('\t'); |
andrewboyson | 140:9000ea70b220 | 202 | HttpAddInt32AsHex(MsTimerCount - records[i].elapsed); |
andrewboyson | 167:3ba4e3c49631 | 203 | HttpAddChar('\t'); |
andrewboyson | 140:9000ea70b220 | 204 | HttpAddInt32AsHex(records[i].ip); |
andrewboyson | 167:3ba4e3c49631 | 205 | HttpAddChar('\t'); |
andrewboyson | 140:9000ea70b220 | 206 | for (int b = 0; b < 6; b++) HttpAddByteAsHex(records[i].mac[b]); |
andrewboyson | 140:9000ea70b220 | 207 | HttpAddChar('\n'); |
andrewboyson | 140:9000ea70b220 | 208 | } |
andrewboyson | 140:9000ea70b220 | 209 | } |
andrewboyson | 140:9000ea70b220 | 210 | } |
andrewboyson | 60:1d8c7a1e7483 | 211 | static void clear(struct record* pr) |
andrewboyson | 60:1d8c7a1e7483 | 212 | { |
andrewboyson | 60:1d8c7a1e7483 | 213 | pr->state = STATE_EMPTY; |
andrewboyson | 60:1d8c7a1e7483 | 214 | } |
andrewboyson | 60:1d8c7a1e7483 | 215 | static void clearCache(struct record* pr) |
andrewboyson | 60:1d8c7a1e7483 | 216 | { |
andrewboyson | 133:a37eb35a03f1 | 217 | if (MsTimerRelative(pr->elapsed, CACHE_TIMEOUT_MS)) clear(pr); |
andrewboyson | 60:1d8c7a1e7483 | 218 | } |
andrewboyson | 60:1d8c7a1e7483 | 219 | static void retry(struct record* pr) |
andrewboyson | 60:1d8c7a1e7483 | 220 | { |
andrewboyson | 133:a37eb35a03f1 | 221 | if (pr->state == STATE_SENT && MsTimerRelative(pr->elapsed, REPLY_TIMEOUT_MS)) |
andrewboyson | 60:1d8c7a1e7483 | 222 | { |
andrewboyson | 60:1d8c7a1e7483 | 223 | if (pr->tries < SEND_ATTEMPTS) |
andrewboyson | 60:1d8c7a1e7483 | 224 | { |
andrewboyson | 60:1d8c7a1e7483 | 225 | pr->state = STATE_WANT; |
andrewboyson | 93:580fc113d9e9 | 226 | pr->elapsed = MsTimerCount; |
andrewboyson | 60:1d8c7a1e7483 | 227 | pr->tries++; |
andrewboyson | 60:1d8c7a1e7483 | 228 | } |
andrewboyson | 60:1d8c7a1e7483 | 229 | else |
andrewboyson | 60:1d8c7a1e7483 | 230 | { |
andrewboyson | 60:1d8c7a1e7483 | 231 | clear(pr); |
andrewboyson | 60:1d8c7a1e7483 | 232 | } |
andrewboyson | 60:1d8c7a1e7483 | 233 | } |
andrewboyson | 60:1d8c7a1e7483 | 234 | } |
andrewboyson | 60:1d8c7a1e7483 | 235 | static void sendRequest(struct record* pr) |
andrewboyson | 60:1d8c7a1e7483 | 236 | { |
andrewboyson | 60:1d8c7a1e7483 | 237 | if (!ArpResolveRequestFlag) |
andrewboyson | 60:1d8c7a1e7483 | 238 | { |
andrewboyson | 60:1d8c7a1e7483 | 239 | if (pr->state == STATE_WANT) |
andrewboyson | 60:1d8c7a1e7483 | 240 | { |
andrewboyson | 60:1d8c7a1e7483 | 241 | if (Ar4Trace) |
andrewboyson | 60:1d8c7a1e7483 | 242 | { |
andrewboyson | 60:1d8c7a1e7483 | 243 | LogTimeF("AR4 Send request for MAC from IP4 "); |
andrewboyson | 187:122fc1996c86 | 244 | Ip4AddrLog(pr->ip); |
andrewboyson | 60:1d8c7a1e7483 | 245 | Log("\r\n"); |
andrewboyson | 60:1d8c7a1e7483 | 246 | } |
andrewboyson | 60:1d8c7a1e7483 | 247 | ArpAddressToResolve = pr->ip; |
andrewboyson | 60:1d8c7a1e7483 | 248 | ArpResolveRequestFlag = true; |
andrewboyson | 60:1d8c7a1e7483 | 249 | pr->state = STATE_SENT; |
andrewboyson | 93:580fc113d9e9 | 250 | pr->elapsed = MsTimerCount; |
andrewboyson | 60:1d8c7a1e7483 | 251 | return; |
andrewboyson | 60:1d8c7a1e7483 | 252 | } |
andrewboyson | 60:1d8c7a1e7483 | 253 | } |
andrewboyson | 60:1d8c7a1e7483 | 254 | } |
andrewboyson | 60:1d8c7a1e7483 | 255 | void Ar4Main() |
andrewboyson | 60:1d8c7a1e7483 | 256 | { |
andrewboyson | 60:1d8c7a1e7483 | 257 | static int i = -1; |
andrewboyson | 60:1d8c7a1e7483 | 258 | i++; |
andrewboyson | 60:1d8c7a1e7483 | 259 | if (i >= RECORDS_COUNT) i = 0; |
andrewboyson | 60:1d8c7a1e7483 | 260 | |
andrewboyson | 60:1d8c7a1e7483 | 261 | struct record* pr = &records[i]; |
andrewboyson | 60:1d8c7a1e7483 | 262 | |
andrewboyson | 60:1d8c7a1e7483 | 263 | clearCache (pr); |
andrewboyson | 60:1d8c7a1e7483 | 264 | retry (pr); |
andrewboyson | 60:1d8c7a1e7483 | 265 | sendRequest(pr); |
andrewboyson | 60:1d8c7a1e7483 | 266 | } |
andrewboyson | 60:1d8c7a1e7483 | 267 | void Ar4Init() |
andrewboyson | 60:1d8c7a1e7483 | 268 | { |
andrewboyson | 60:1d8c7a1e7483 | 269 | for (int i = 0; i < RECORDS_COUNT; i++) |
andrewboyson | 60:1d8c7a1e7483 | 270 | { |
andrewboyson | 60:1d8c7a1e7483 | 271 | struct record* pr = &records[i]; |
andrewboyson | 60:1d8c7a1e7483 | 272 | clear(pr); |
andrewboyson | 60:1d8c7a1e7483 | 273 | } |
andrewboyson | 60:1d8c7a1e7483 | 274 | } |