aprx-2.08.svn593/0000755000175000017500000000000012562417650012465 5ustar colincolinaprx-2.08.svn593/historydb.c0000644000175000017500000004211512314016324014627 0ustar colincolin/******************************************************************** * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * ********************************************************************/ #include "aprx.h" #ifndef DISABLE_IGATE #include #include #include int lastposition_storetime = 3600; // 1 hour static historydb_t **_dbs; static int _dbs_count; void historydb_nopos(void) {} /* profiler call counter items */ void historydb_nointerest(void) {} void historydb_hashmatch(void) {} void historydb_keymatch(void) {} void historydb_dataupdate(void) {} // Single aprx wide alloc system static cellarena_t *historydb_cells; const int historydb_cellsize = sizeof(struct history_cell_t); const int historydb_cellalign = __alignof__(struct history_cell_t); void historydb_init(void) { // printf("historydb_init() sizeof(mutex)=%d sizeof(rwlock)=%d\n", // sizeof(pthread_mutex_t), sizeof(rwlock_t)); // _dbs = malloc(sizeof(void*)); // _dbs_count = 0; historydb_cells = cellinit( "historydb", historydb_cellsize, historydb_cellalign, CELLMALLOC_POLICY_FIFO, 32 /* 32 kB */, 0 /* minfree */ ); } /* new instance - for new digipeater tx */ historydb_t *historydb_new(void) { historydb_t *db = calloc(1, sizeof(*db)); ++_dbs_count; _dbs = realloc(_dbs, sizeof(void*)*_dbs_count); _dbs[_dbs_count-1] = db; return db; } /* Called only under WR-LOCK */ void historydb_free(struct history_cell_t *p) { if (p->packet != p->packetbuf) free(p->packet); if (p->last_heard != p->last_heard_buf) free(p->last_heard); --p->db->historydb_cellgauge; cellfree( historydb_cells, p ); } /* Called only under WR-LOCK */ struct history_cell_t *historydb_alloc(historydb_t *db, int packet_len) { struct history_cell_t *ret = cellmalloc( historydb_cells ); if (!ret) return NULL; ++db->historydb_cellgauge; ret->db = db; ret->last_heard = ((top_interfaces_group <= MAX_IF_GROUP) ? ret->last_heard_buf : malloc(sizeof(time_t)*top_interfaces_group)); return ret; } /* * The historydb_atend() does exist primarily to make valgrind * happy about lost memory object tracking. */ void historydb_atend(void) { int j, i; for (j = 0; j < _dbs_count; ++j) { historydb_t *db = _dbs[j]; struct history_cell_t *hp, *hp2; for (i = 0; i < HISTORYDB_HASH_MODULO; ++i) { hp = db->hash[i]; while (hp) { hp2 = hp->next; historydb_free(hp); hp = hp2; } } } } void historydb_dump_entry(FILE *fp, const struct history_cell_t *hp) { fprintf(fp, "%ld\t", hp->arrivaltime); (void)fwrite(hp->key, hp->keylen, 1, fp); fprintf(fp, "\t"); fprintf(fp, "%d\t%d\t", hp->packettype, hp->flags); fprintf(fp, "%f\t%f\t", hp->lat, hp->lon); fprintf(fp, "%d\t", hp->packetlen); (void)fwrite(hp->packet, hp->packetlen, 1, fp); fprintf(fp, "\n"); /* newline */ } void historydb_dump(const historydb_t *db, FILE *fp) { /* Dump the historydb out on text format */ int i; struct history_cell_t *hp; time_t expirytime = tick.tv_sec - lastposition_storetime; for ( i = 0; i < HISTORYDB_HASH_MODULO; ++i ) { hp = db->hash[i]; for ( ; hp ; hp = hp->next ) if (timecmp(hp->arrivaltime, expirytime) > 0) historydb_dump_entry(fp, hp); } } static int foldhash( const unsigned int h1 ) { unsigned int h2 = h1 ^ (h1 >> 7) ^ (h1 >> 14); /* fold hash bits.. */ return (h2 % HISTORYDB_HASH_MODULO); } /* insert... */ history_cell_t *historydb_insert(historydb_t *db, const struct pbuf_t *pb) { return historydb_insert_(db, pb, 0); } history_cell_t *historydb_insert_(historydb_t *db, const struct pbuf_t *pb, const int insertall) { int i; unsigned int h1; int isdead = 0, keylen; struct history_cell_t **hp, *cp, *cp1; time_t expirytime = tick.tv_sec - lastposition_storetime; char keybuf[CALLSIGNLEN_MAX+2]; char *s; // (pb->flags & F_HASPOS) <-- that indicates that at parse time // the packet either had a position, or // a position information was supplemented // to it via historydb lookup if (!insertall && !(pb->packettype & T_POSITION)) { // <-- packet has position data ++db->historydb_noposcount; historydb_nopos(); /* debug thing -- profiling counter */ return NULL; /* No positional data... */ } /* NOTE: Parser does set on MESSAGES the RECIPIENTS ** location if such is known! We do not want them... ** .. and several other cases where packet has no ** positional data in it, but source callsign may ** have previous entry with data. */ /* NOTE2: We could use pb->srcname, and pb->srcname_len here, ** but then we would not know if this is a "kill-item" */ keybuf[CALLSIGNLEN_MAX] = 0; if (pb->packettype & T_OBJECT) { /* Pick object name ";item *" */ memcpy( keybuf, pb->info_start+1, CALLSIGNLEN_MAX+1); keybuf[CALLSIGNLEN_MAX+1] = 0; s = strchr(keybuf, '*'); if (s) *s = 0; else { s = strchr(keybuf, '_'); // kill an object! if (s) { *s = 0; isdead = 1; } } s = keybuf + strlen(keybuf); for ( ; s > keybuf; --s ) { // tail space padded.. if (*s == ' ') *s = ' '; else break; } } else if (pb->packettype & T_ITEM) { // Pick item name ") . . . !" or ") . . . _" memcpy( keybuf, pb->info_start+1, CALLSIGNLEN_MAX+1); keybuf[CALLSIGNLEN_MAX+1] = 0; s = strchr(keybuf, '!'); if (s) *s = 0; else { s = strchr(keybuf, '_'); // kill an item! if (s) { *s = 0; isdead = 1; } } } else if (pb->packettype & T_MESSAGE) { // Pick originator callsign memcpy( keybuf, pb->data, CALLSIGNLEN_MAX) ; s = strchr(keybuf, '>'); if (s) *s = 0; } else if (pb->packettype & T_POSITION) { // Pick originator callsign memcpy( keybuf, pb->data, CALLSIGNLEN_MAX) ; s = strchr(keybuf, '>'); if (s) *s = 0; } else { if (insertall) { // Pick originator callsign memcpy( keybuf, pb->data, CALLSIGNLEN_MAX) ; s = strchr(keybuf, '>'); if (s) *s = 0; } else { historydb_nointerest(); // debug thing -- a profiling counter return NULL; // Not a packet with positional data, not interested in... } } keylen = strlen(keybuf); ++db->historydb_inserts; h1 = keyhash(keybuf, keylen, 0); i = foldhash(h1); if (debug > 1) printf(" key='%s' hash=%d", keybuf, i); cp = cp1 = NULL; hp = &db->hash[i]; // scan the hash-bucket chain, and do incidential obsolete data discard while (( cp = *hp )) { if (timecmp(cp->arrivaltime, expirytime) < 0) { // OLD... *hp = cp->next; cp->next = NULL; historydb_free(cp); continue; } if ( (cp->hash1 == h1)) { // Hash match, compare the key historydb_hashmatch(); // debug thing -- a profiling counter ++db->historydb_hashmatches; if ( cp->keylen == keylen && (memcmp(cp->key, keybuf, keylen) == 0) ) { // Key match! historydb_keymatch(); // debug thing -- a profiling counter ++db->historydb_keymatches; if (isdead) { // Remove this key.. *hp = cp->next; cp->next = NULL; historydb_free(cp); continue; } else { historydb_dataupdate(); // debug thing -- a profiling counter // Update the data content cp1 = cp; if (pb->flags & F_HASPOS) { // Update coordinate, if available cp->lat = pb->lat; cp->coslat = pb->cos_lat; cp->lon = pb->lng; cp->positiontime = pb->t; } cp->packettype = pb->packettype; cp->flags |= pb->flags; cp->arrivaltime = pb->t; cp->flags = pb->flags; cp->packetlen = pb->packet_len; cp->last_heard[pb->source_if_group] = pb->t; if ( cp->packet != cp->packetbuf ) free( cp->packet ); cp->packet = cp->packetbuf; // default case if ( cp->packetlen > sizeof(cp->packetbuf) ) { // Needs bigger buffer than pre-allocated one, // thus it retrieves that one from heap. cp->packet = malloc( cp->packetlen ); } memcpy( cp->packet, pb->data, cp->packetlen ); } } } // .. else no match, advance hp.. hp = &(cp -> next); } if (!cp1 && !isdead) { // Not found on this chain, append it! cp = historydb_alloc(db, pb->packet_len); cp->next = NULL; memcpy(cp->key, keybuf, keylen); cp->key[keylen] = 0; /* zero terminate */ cp->keylen = keylen; cp->hash1 = h1; cp->lat = pb->lat; cp->coslat = pb->cos_lat; cp->lon = pb->lng; cp->arrivaltime = pb->t; cp->packettype = pb->packettype; cp->flags = pb->flags; cp->last_heard[pb->source_if_group] = pb->t; if (pb->flags & F_HASPOS) cp->positiontime = pb->t; cp->packetlen = pb->packet_len; cp->packet = cp->packetbuf; // default case if (cp->packetlen > sizeof(cp->packetbuf)) { // Needs bigger buffer than pre-allocated one, // thus it retrieves that one from heap. cp->packet = malloc( cp->packetlen ); } // Initial value is 32.0 tokens to permit // digipeat a packet source at the first // time it has been heard -- including to // possible multiple transmitters. Within // about 5 seconds this will be dropped // down to max burst rate of the srcratefilter // parameter. This code does not know how // many interfaces there are... cp->tokenbucket = 32.0; *hp = cp; } return *hp; } history_cell_t *historydb_insert_heard(historydb_t *db, const struct pbuf_t *pb) { int i; unsigned int h1; int keylen; struct history_cell_t **hp, *cp, *cp1; time_t expirytime = tick.tv_sec - lastposition_storetime; char keybuf[CALLSIGNLEN_MAX+2]; char *s; /* NOTE: Parser does set on MESSAGES the RECIPIENTS ** location if such is known! We do not want them... ** .. and several other cases where packet has no ** positional data in it, but source callsign may ** have previous entry with data. */ /* NOTE2: We could use pb->srcname, and pb->srcname_len here, ** but then we would not know if this is a "kill-item" */ keybuf[CALLSIGNLEN_MAX+1] = 0; if (pb->packettype & T_OBJECT) { historydb_nointerest(); // debug thing -- a profiling counter if (debug > 1) printf(" .. objects not interested\n"); return NULL; // Not interested in ";objects :" } else if (pb->packettype & T_ITEM) { historydb_nointerest(); // debug thing -- a profiling counter if (debug > 1) printf(" .. items not interested\n"); return NULL; // Not interested in ")items..." } else if (pb->packettype & T_MESSAGE) { // Pick originator callsign //memcpy( keybuf, pb->data, CALLSIGNLEN_MAX) ; //s = strchr(keybuf, '>'); //if (s) *s = 0; memcpy(keybuf, pb->srcname, pb->srcname_len); keybuf[pb->srcname_len] = 0; } else if (pb->packettype & T_POSITION) { // Something with a position (but not an item or an object) //memcpy( keybuf, pb->data, CALLSIGNLEN_MAX) ; //s = strchr(keybuf, '>'); //if (s) *s = 0; memcpy(keybuf, pb->srcname, pb->srcname_len); keybuf[pb->srcname_len] = 0; } else { if (debug > 1) printf(" .. other not interested\n"); historydb_nointerest(); // debug thing -- a profiling counter return NULL; // Not a packet with positional data, not interested in... } keylen = strlen(keybuf); ++db->historydb_inserts; h1 = keyhash(keybuf, keylen, 0); i = foldhash(h1); if (debug > 1) printf(" key='%s' hash=%d", keybuf, i); cp1 = NULL; hp = &db->hash[i]; // scan the hash-bucket chain, and do incidential obsolete data discard while (( cp = *hp ) != NULL) { if (timecmp(cp->arrivaltime, expirytime) < 0) { // OLD... if (debug > 1) printf(" .. dropping old record\n"); *hp = cp->next; cp->next = NULL; historydb_free(cp); continue; } if ( (cp->hash1 == h1)) { // Hash match, compare the key historydb_hashmatch(); // debug thing -- a profiling counter ++db->historydb_hashmatches; if (debug > 1) printf(" .. found matching hash"); if ( cp->keylen == keylen && (memcmp(cp->key, keybuf, keylen) == 0) ) { // Key match! if (debug > 1) printf(" .. found matching key!\n"); historydb_keymatch(); // debug thing -- a profiling counter ++db->historydb_keymatches; historydb_dataupdate(); // debug thing -- a profiling counter // Update the data content cp1 = cp; if (pb->flags & F_HASPOS) { // Update coordinate, if available cp->lat = pb->lat; cp->coslat = pb->cos_lat; cp->lon = pb->lng; cp->positiontime = pb->t; cp->arrivaltime = pb->t; } cp->flags |= pb->flags; // Track packet source timestamps cp->last_heard[pb->source_if_group] = pb->t; // Don't save a message on top of positional packet if (!(pb->packettype & T_MESSAGE)) { cp->packettype = pb->packettype; cp->arrivaltime = pb->t; cp->flags = pb->flags; cp->packetlen = pb->packet_len; if ( cp->packet != cp->packetbuf ) free( cp->packet ); cp->packet = cp->packetbuf; // default case if ( cp->packetlen > sizeof(cp->packetbuf) ) { // Needs bigger buffer than pre-allocated one, // thus it retrieves that one from heap. cp->packet = malloc( cp->packetlen ); } memcpy( cp->packet, pb->data, cp->packetlen ); } } } // .. else no match, advance hp.. hp = &(cp -> next); } if (!cp1) { if (debug > 1) printf(" .. inserting new history entry.\n"); // Not found on this chain, append it! cp = historydb_alloc(db, pb->packet_len); cp->next = NULL; memcpy(cp->key, keybuf, keylen); cp->key[keylen] = 0; /* zero terminate */ cp->keylen = keylen; cp->hash1 = h1; cp->lat = pb->lat; cp->coslat = pb->cos_lat; cp->lon = pb->lng; cp->arrivaltime = pb->t; cp->packettype = pb->packettype; cp->flags = pb->flags; cp->last_heard[pb->source_if_group] = pb->t; if (pb->flags & F_HASPOS) cp->positiontime = pb->t; cp->packetlen = pb->packet_len; cp->packet = cp->packetbuf; // default case if (cp->packetlen > sizeof(cp->packetbuf)) { // Needs bigger buffer than pre-allocated one, // thus it retrieves that one from heap. cp->packet = malloc( cp->packetlen ); } *hp = cp; } else return cp1; // != NULL return *hp; } /* lookup... */ history_cell_t *historydb_lookup(historydb_t *db, const char *keybuf, const int keylen) { int i; unsigned int h1; struct history_cell_t *cp; // validity is 5 minutes shorter than expiration time.. time_t validitytime = tick.tv_sec - lastposition_storetime + 5*60; ++db->historydb_lookups; h1 = keyhash(keybuf, keylen, 0); i = foldhash(h1); cp = db->hash[i]; if (debug > 1) printf("historydb_lookup(%*s) -> i=%d", keylen, keybuf, i); for ( ; cp != NULL ; cp = cp->next ) { if ( (cp->hash1 == h1) && // Hash match, compare the key (cp->keylen == keylen) ) { if (debug > 1) printf(" .. hash match"); if (memcmp(cp->key, keybuf, keylen) == 0) { if (debug > 1) printf(" .. key match"); // Key match! if (timecmp(cp->arrivaltime, validitytime) > 0) { if (debug > 1) printf(" .. and not too old\n"); return cp; } } } } if (debug > 1) printf(" .. no match\n"); return NULL; } /* * The historydb_cleanup() exists to purge too old data out of * the database at regular intervals. Call this about once a minute. */ static void historydb_cleanup(historydb_t *db) { struct history_cell_t **hp, *cp; int i, cleancount = 0; if (debug > 1) printf("historydb_cleanup() "); time_t expirytime = tick.tv_sec - lastposition_storetime; for (i = 0; i < HISTORYDB_HASH_MODULO; ++i) { hp = &db->hash[i]; // multiple locks ? one for each bucket, or for a subset of buckets ? while (( cp = *hp )) { if (timecmp(cp->arrivaltime, expirytime) < 0) { // OLD... *hp = cp->next; cp->next = NULL; historydb_free(cp); ++cleancount; if (debug > 1) printf(" drop(%p) i=%d", cp, i); } else { /* No expiry, just advance the pointer */ hp = &(cp -> next); } } } if (debug > 1) printf(" .. done.\n"); } static time_t next_cleanup_time; int historydb_prepoll(struct aprxpolls *app) { return 0; } int historydb_postpoll(struct aprxpolls *app) { int i; // Limit next cleanup to be at most 60 second in future // (just in case the system time jumped back) if (next_cleanup_time >= tick.tv_sec+61) { next_cleanup_time = tick.tv_sec + 60; } if (next_cleanup_time >= tick.tv_sec) return 0; next_cleanup_time = tick.tv_sec + 60; // A minute from now.. for (i = 0; i < _dbs_count; ++i) { historydb_cleanup(_dbs[i]); } return 0; } #endif aprx-2.08.svn593/aprx.h0000644000175000017500000006360712313325035013611 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "config.h" #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_TIME_H # include #endif #include #ifdef HAVE_STDLIB_H # include #endif #define __need_size_t #define __need_NULL #ifdef HAVE_STDDEF_H # include #endif #ifdef HAVE_STDARG_H #include #else #include #endif #if defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD) #include pthread_t aprsis_thread; pthread_attr_t pthr_attrs; #endif #ifdef _FOR_VALGRIND_ #define strdup aprx_strdup #define strcmp aprx_strcmp #define strncmp aprx_strncmp #define memcmp aprx_memcmp #define memcpy aprx_memcpy #define memchr aprx_memchr #define memrchr aprx_memrchr #define strlen aprx_strlen #define strcpy aprx_strcpy #define strncpy aprx_strncpy #define strchr aprx_strchr // Single char at the time naive implementations for valgrind runs extern int memcmp(const void *p1, const void *p2, size_t n); extern void *memcpy(void *dest, const void *src, size_t n); extern size_t strlen(const char *p); extern char *strdup(const char *s); extern int strcmp(const char *s1, const char *s2); extern int strncmp(const char *s1, const char *s2, size_t n); extern char *strcpy(char *dest, const char *src); extern char *strncpy(char *dest, const char *src, size_t n); extern void *memchr(const void *s, int c, size_t n); extern char *strchr(const char *s, int c); // extern declarators for standard functions extern void *memset(void *s, int c, size_t n); extern char *strerror(const int n); extern void *memmove(void *dest, const void *src, size_t n); extern char *strtok(char *str, const char *delim); extern int strcasecmp(const char *s1, const char *s2); #else #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #endif extern void *memrchr(const void *s, int c, size_t n); #include #include #include #include #ifdef HAVE_ALLOCA_H # include #endif #include #ifdef HAVE_STDINT_H #include #endif #include #include #include #include /* Radio interface groups on igate receiption history tracking. * Value range: 1 to MAX_IF_GROUP-1. * Value 0 is reserved for APRSIS. */ #define MAX_IF_GROUP 4 #define CALLSIGNLEN_MAX 9 struct aprxpolls; // forward declarator #include "cellmalloc.h" #include "historydb.h" #include "keyhash.h" #include "pbuf.h" #if 0 #define static /*ignore statics during debug */ #endif struct aprx_interface; // Forward declarator struct configfile { const char *name; FILE *fp; int linenum_i; // internal linenum int linenum; // externally presented, first line of folded multilines char buf[8010]; }; /* aprxpolls.c */ struct aprxpolls { struct pollfd *polls; int pollcount; int pollsize; struct timeval next_timeout; }; #define APRXPOLLS_INIT { NULL, 0, 0, {0,0} } extern int aprxpolls_millis(struct aprxpolls *app); extern void aprxpolls_reset(struct aprxpolls *app); extern struct pollfd *aprxpolls_new(struct aprxpolls *app); extern void aprxpolls_free(struct aprxpolls *app); /* aprx.c */ #ifndef DISABLE_IGATE extern const char *aprsis_login; #endif extern int die_now; extern const char *mycall; extern const char *tocall; extern const uint8_t tocall25[7]; extern float myloc_lat; extern float myloc_coslat; extern float myloc_lon; extern const char *myloc_latstr; extern const char *myloc_lonstr; extern void fd_nonblockingmode(int fd); extern const char *swname; extern const char *swversion; extern void timetick(void); extern struct timeval now; // Public wall lock time that can jump around extern struct timeval tick; // Monotonic clock, progresses regularly from boot. NOT wall clock time. extern int time_reset; // Set during ONE call cycle of prepolls extern int debug; extern int verbout; extern int erlangout; extern const char *rflogfile; extern const char *aprxlogfile; extern const char *dprslogfile; extern const char *erlanglogfile; extern const char *pidfile; extern void printtime(char *buf, int buflen); extern void aprx_syslog_init(const char *syslog_fac); #ifdef HAVE_STDARG_H #ifdef __STDC__ extern void aprxlog(const char *fmt, ...); #endif #else /* VARARGS */ extern void aprxlog(va_list); #endif /* netresolver.c */ extern void netresolv_start(void); // separate thread working on this! extern void netresolv_stop(void); struct netresolver { char const *hostname; char const *port; time_t re_resolve_time; struct addrinfo ai; struct sockaddr sa; }; extern struct netresolver *netresolv_add(const char *hostname, const char *port); /* ttyreader.c */ typedef enum { LINETYPE_KISS, /* all KISS variants without CRC on line */ LINETYPE_KISSSMACK, /* KISS/SMACK variants with CRC on line */ LINETYPE_KISSFLEXNET, /* KISS/FLEXNET with CRC on line */ LINETYPE_KISSBPQCRC, /* BPQCRC - really XOR sum of data bytes, also "AEACRC" */ LINETYPE_TNC2, /* text line from TNC2 in monitor mode */ LINETYPE_AEA, /* not implemented... */ LINETYPE_DPRSGW /* Special DPRS RX GW mode */ } LineType; typedef enum { KISSSTATE_SYNCHUNT = 0, KISSSTATE_COLLECTING, KISSSTATE_KISSFESC } KissState; struct serialport { int fd; /* UNIX fd of the port */ struct timeval wait_until; time_t last_read_something; /* Used by serial port functionality watchdog */ int read_timeout; /* seconds */ int poll_millis; /* milliseconds (0 = none.) */ LineType linetype; KissState kissstate; /* state for KISS frame reader, also for line collector */ /* NOTE: The smack_probe is separate on all ** sub-tnc:s on SMACK loop */ time_t smack_probe[8]; /* if need to send SMACK probe, use this to limit their transmit frequency. */ int smack_subids; /* bitset; 0..7; could use char... */ struct termios tio; /* tcsetattr(fd, TCSAFLUSH, &tio) */ /* stty speed 19200 sane clocal pass8 min 1 time 5 -hupcl ignbrk -echo -ixon -ixoff -icanon */ const char *ttyname; /* "/dev/ttyUSB1234-bar22-xyz7" -- Linux TTY-names can be long.. */ const char *ttycallsign[16]; /* callsign */ const void *netax25[16]; char *initstring[16]; /* optional init-string to be sent to the TNC, NULL OK */ int initlen[16]; /* .. as it can have even NUL-bytes, length is important! */ struct aprx_interface *interface[16]; uint8_t rdbuf[2000]; /* buffering area for raw stream read */ int rdlen, rdcursor; /* rdlen = last byte in buffer, rdcursor = next to read. When rdlen == 0, buffer is empty. */ time_t rdline_time; /* last time something was added there */ uint8_t rdline[2000]; /* processed into lines/records */ int rdlinelen; /* length of this record */ uint8_t wrbuf[4000]; /* buffering area for raw stream read */ int wrlen, wrcursor; /* wrlen = last byte in buffer, wrcursor = next to write. When wrlen == 0, buffer is empty. */ void *dprsgw; /* opaque DPRS GW data */ }; extern int ttyreader_prepoll(struct aprxpolls *); extern int ttyreader_postpoll(struct aprxpolls *); extern void ttyreader_init(void); // Old style init: ttyreader_serialcfg() extern const char *ttyreader_serialcfg(struct configfile *cf, char *param1, char *str); // New style init: ttyreader_new() extern struct serialport *ttyreader_new(void); extern void ttyreader_register(struct serialport *tty); extern int ttyreader_getc(struct serialport *tty); // extern void ttyreader_setlineparam(struct serialport *tty, const char *ttyname, const int baud, int const kisstype); // extern void ttyreader_setkissparams(struct serialport *tty, const int tncid, const char *callsign, const int timeout); extern int ttyreader_parse_ttyparams(struct configfile *cf, struct serialport *tty, char *str); extern void ttyreader_linewrite(struct serialport *S); extern int ttyreader_parse_nullparams(struct configfile *cf, struct serialport *tty, char *str); extern void hexdumpfp(FILE *fp, const uint8_t *buf, const int len, int axaddr); extern void aprx_cfmakeraw(struct termios *, int f); extern void tv_timerbounds(const char *, struct timeval *tv, const int margin, void (*resetfunc)(void*), void *resetarg ); extern void tv_timeradd_millis(struct timeval *res, struct timeval * const a, const int millis); extern void tv_timeradd_seconds(struct timeval *res, struct timeval * const a, const int seconds); extern int tv_timerdelta_millis(struct timeval * const _now, struct timeval * const _target); extern int tv_timercmp(struct timeval * const a, struct timeval * const b); extern int timecmp(time_t a, time_t b); /* ax25.c */ extern int ax25_to_tnc2_fmtaddress(char *dest, const uint8_t *src, int markflag); extern int ax25_to_tnc2(const struct aprx_interface *aif, const char *portname, const int tncid, const int cmdbyte, const uint8_t *frame, const int framelen); extern void ax25_filter_add(const char *p1, const char *p2); extern int ax25_format_to_tnc(const uint8_t *frame, const int framelen, char *tnc2buf, const int tnc2buflen, int *frameaddrlen, int *tnc2addrlen, int *is_aprs, int *ui_pid); extern int parse_ax25addr(uint8_t ax25[7], const char *text, int ssidflags); #ifndef DISABLE_IGATE /* aprsis.c */ extern int aprsis_add_server(const char *server, const char *port); extern int aprsis_set_heartbeat_timeout(const int tout); extern int aprsis_set_filter(const char *filter); extern int aprsis_set_login(const char *login); #define qTYPE_IGATED 'R' #define qTYPE_LOCALGEN 'S' extern int aprsis_queue(const char *addr, int addrlen, const char qtype, const char *gwcall, const char *text, int textlen); extern int aprsis_prepoll(struct aprxpolls *app); extern int aprsis_postpoll(struct aprxpolls *app); extern void aprsis_init(void); extern void aprsis_start(void); extern void aprsis_stop(void); extern int aprsis_config(struct configfile *cf); extern char * const aprsis_loginid; #endif /* beacon.c */ extern int beacon_prepoll(struct aprxpolls *app); extern int beacon_postpoll(struct aprxpolls *app); extern int beacon_config(struct configfile *cf); extern void beacon_childexit(int pid); /* config.c */ extern void *readconfigline(struct configfile *cf); extern int configline_is_comment(struct configfile *cf); extern int readconfig(const char *cfgfile); extern char *config_SKIPSPACE(char *Y); extern char *config_SKIPTEXT(char *Y, int *lenp); extern void config_STRLOWER(char *Y); extern void config_STRUPPER(char *Y); extern int validate_callsign_input(char *callsign, int strict); // this modifies callsign string! extern int config_parse_interval(const char *par, int *resultp); extern int config_parse_boolean(const char *par, int *resultp); extern const char *scan_int(const char *p, int len, int*val, int*seen_space); extern int validate_degmin_input(const char *s, int maxdeg); /* dprsgw.c */ extern int dprsgw_pulldprs(struct serialport *S); extern int dprsgw_prepoll(struct aprxpolls *app); extern int dprsgw_postpoll(struct aprxpolls *app); /* erlang.c */ extern void erlang_init(const char *syslog_facility_name); extern void erlang_start(int do_create); extern int erlang_prepoll(struct aprxpolls *app); extern int erlang_postpoll(struct aprxpolls *app); /* igate.c */ #ifndef DISABLE_IGATE extern void igate_start(void); extern void igate_from_aprsis(const char *ax25, int ax25len); extern void igate_to_aprsis(const char *portname, const int tncid, const char *tnc2buf, int tnc2addrlen, int tnc2len, const int discard, const int strictax25); extern void enable_tx_igate(const char *, const char *); #endif extern void rflog(const char *portname, char direction, int discard, const char *tnc2buf, int tnc2len); extern const char *tnc2_verify_callsign_format(const char *t, int starok, int strictax25, const char *e); /* netax25.c */ #ifdef PF_AX25 /* PF_AX25 exists -- highly likely a Linux system ! */ extern void netax25_init(void); extern void netax25_start(void); extern const void* netax25_open(const char *ifcallsign); extern int netax25_prepoll(struct aprxpolls *); extern int netax25_postpoll(struct aprxpolls *); extern void * netax25_addrxport(const char *callsign, const struct aprx_interface *aif); extern void netax25_sendax25(const void *nax25, const void *ax25, int ax25len); extern void netax25_sendto(const void *nax25, const uint8_t *axaddr, const int axaddrlen, const char *axdata, const int axdatalen); #endif /* telemetry.c */ #define USE_ONE_MINUTE_DATA 0 extern void telemetry_start(void); extern int telemetry_prepoll(struct aprxpolls *app); extern int telemetry_postpoll(struct aprxpolls *app); extern int telemetry_config(struct configfile *cf); typedef enum { ERLANG_RX, ERLANG_DROP, ERLANG_TX } ErlangMode; extern void erlang_add(const char *portname, ErlangMode erl, int bytes, int packets); extern void erlang_set(const char *portname, int bytes_per_minute); extern int erlangsyslog; extern int erlanglog1min; extern const char *erlang_backingstore; /* The struct erlangline is shared in between the aprx, and erlang reporter application: aprx-stat */ struct erlang_rxtxbytepkt { long packets_rx, packets_rxdrop, packets_tx ; long bytes_rx, bytes_rxdrop, bytes_tx ; time_t update; }; struct erlangline { const void *refp; int index; char name[31]; uint8_t __subport; time_t last_update; int erlang_capa; /* bytes, 1 minute */ struct erlang_rxtxbytepkt SNMP; /* SNMPish counters */ #ifdef ERLANGSTORAGE struct erlang_rxtxbytepkt erl1m; /* 1 minute erlang period */ struct erlang_rxtxbytepkt erl10m; /* 10 minute erlang period */ struct erlang_rxtxbytepkt erl60m; /* 60 minute erlang period */ #else #if (USE_ONE_MINUTE_DATA == 1) struct erlang_rxtxbytepkt erl1m; /* 1 minute erlang period */ #else struct erlang_rxtxbytepkt erl10m; /* 10 minute erlang period */ #endif #endif #ifdef ERLANGSTORAGE int e1_cursor, e1_max; /* next store point + max cursor index */ int e10_cursor, e10_max; int e60_cursor, e60_max; #else #if (USE_ONE_MINUTE_DATA == 1) int e1_cursor, e1_max; /* next store point + max cursor index */ #else int e10_cursor, e10_max; #endif #endif #ifdef ERLANGSTORAGE #define APRXERL_1M_COUNT (60*24) // 1 day of 1 minute data #define APRXERL_10M_COUNT (60*24*7) // 1 week of 10 minute data #define APRXERL_60M_COUNT (24*31*3) // 3 months of hourly data struct erlang_rxtxbytepkt e1[APRXERL_1M_COUNT]; struct erlang_rxtxbytepkt e10[APRXERL_10M_COUNT]; struct erlang_rxtxbytepkt e60[APRXERL_60M_COUNT]; #else /* EMBEDDED */ /* When making very small memory footprint, like embedding on Linksys WRT54GL ... */ #define APRXERL_1M_COUNT (22) // 22 minutes of 1 minute data #define APRXERL_10M_COUNT (3) // 30 minutes of 10 minute data #if (USE_ONE_MINUTE_DATA == 1) struct erlang_rxtxbytepkt e1[APRXERL_1M_COUNT]; #else struct erlang_rxtxbytepkt e10[APRXERL_10M_COUNT]; #endif #endif }; struct erlanghead { char title[32]; int version; /* format version */ int linecount; time_t last_update; pid_t server_pid; time_t start_time; char mycall[16]; double align_filler; }; #define ERLANGLINE_STRUCT_VERSION ((sizeof(struct erlanghead)<<16)+sizeof(struct erlangline)) extern struct erlanghead *ErlangHead; extern struct erlangline **ErlangLines; extern int ErlangLinesCount; /* dupecheck.c */ typedef struct dupe_record_t { struct dupe_record_t *next; uint32_t hash; time_t t; // creation time time_t t_exp; // expiration time struct pbuf_t *pbuf; // To send packet out of delayed processing, // this pointer must be non-NULL. int16_t seen; // Count of times this packet has been seen // on non-delayed processing. First one will // be sent when pbuf is != NULL. int16_t delayed_seen; // Count of times this packet has been seen // on delayed processing. The packet may get // sent, if "seen" count is zero at delay end. int16_t seen_on_transmitter; // Source of where it was seen is same // as this digipeater transmitter. int16_t refcount; // number of references on this entry int16_t alen; // Address length int16_t plen; // Payload length char addresses[20]; char *packet; char packetbuf[200]; /* 99.9+ % of time this is enough.. */ } dupe_record_t; #define DUPECHECK_DB_SIZE 16 /* Hash index table size - per dupechecker */ typedef struct dupecheck_t { int storetime; struct dupe_record_t *dupecheck_db[DUPECHECK_DB_SIZE]; /* Hash index table */ } dupecheck_t; extern void dupecheck_init(void); /* Inits the dupechecker subsystem */ extern dupecheck_t *dupecheck_new(const int storetime); /* Makes a new dupechecker */ extern dupe_record_t *dupecheck_get(dupe_record_t *dp); // increment refcount extern void dupecheck_put(dupe_record_t *dp); // decrement refcount extern dupe_record_t *dupecheck_aprs(dupecheck_t *dp, const char *addr, const int alen, const char *data, const int dlen); /* aprs checker */ extern dupe_record_t *dupecheck_pbuf(dupecheck_t *dp, struct pbuf_t *pb, const int viscous_delay); /* pbuf checker */ extern int dupecheck_prepoll(struct aprxpolls *app); extern int dupecheck_postpoll(struct aprxpolls *app); /* crc.c */ // kissencoder() needs direct access to CRC tables.. extern const uint16_t crc16_table[256]; extern const uint16_t crc_flex_table[256]; extern uint16_t calc_crc_16(const uint8_t *buf, int n); /* SMACK's CRC-16 */ extern uint16_t calc_crc_flex(const uint8_t *buf, int n); /* FLEXNET's CRC */ extern uint16_t calc_crc_ccitt(uint16_t crc, const uint8_t *buf, int len); // X.25's FCS a.k.a. CRC-CCITT a.k.a. CCITT-CRC extern int check_crc_16(const uint8_t *buf, int n); /* SMACK's CRC-16 */ extern int check_crc_flex(const uint8_t *buf, int n); /* FLEXNET's CRC */ extern int check_crc_ccitt(const uint8_t *buf, int n); /* KISS protocol encoder/decoder specials */ #define KISS_FEND (0xC0) #define KISS_FESC (0xDB) #define KISS_TFEND (0xDC) #define KISS_TFESC (0xDD) extern int kissencoder(void *, int, LineType, const void *, int, int); extern void kiss_kisswrite(struct serialport *S, const int tncid, const uint8_t *ax25raw, const int ax25rawlen); extern int kiss_pullkiss(struct serialport *S); extern void kiss_poll(struct serialport *S); /* digipeater.c */ typedef enum { DIGIRELAY_UNSET, DIGIRELAY_DIGIPEAT, DIGIRELAY_DIGIPEAT_DIRECTONLY, DIGIRELAY_THIRDPARTY } digi_relaytype; struct filter_t; // Forward declarator struct digipeater; // Forward declarator struct tracewide { int maxreq; int maxdone; int is_trace; int nkeys; char **keys; int *keylens; }; struct digipeater_source { struct digipeater *parent; digi_relaytype src_relaytype; struct aprx_interface *src_if; struct filter_t *src_filters; struct tracewide *src_trace; struct tracewide *src_wide; #ifndef DISABLE_IGATE char *via_path; // for APRSIS only char *msg_path; // for APRSIS only uint8_t ax25viapath[7]; // APRSIS uint8_t msgviapath[7]; // APRSIS #endif float tokenbucket; float tbf_increment; float tbf_limit; // Viscous queue is at , but used dupechecker // is -wide, common to all sources in that // digipeater. int viscous_delay; int viscous_queue_size; int viscous_queue_space; struct dupe_record_t **viscous_queue; int sourceregscount; regex_t **sourceregs; int destinationregscount; regex_t **destinationregs; int viaregscount; regex_t **viaregs; int dataregscount; regex_t **dataregs; }; struct digipeater { struct aprx_interface *transmitter; float tokenbucket; // Per transmitter TokenBucket filter float tbf_increment; float tbf_limit; float src_tbf_increment; // Source call specific TokenBucket rules float src_tbf_limit; dupecheck_t *dupechecker; // Per transmitter dupecheck #ifndef DISABLE_IGATE historydb_t *historydb; // Per transmitter HistoryDB #endif const struct tracewide *trace; const struct tracewide *wide; int sourcecount; struct digipeater_source **sources; }; extern int digipeater_prepoll(struct aprxpolls *app); extern int digipeater_postpoll(struct aprxpolls *app); extern int digipeater_config(struct configfile *cf); extern void digipeater_receive(struct digipeater_source *src, struct pbuf_t *pb); extern int digipeater_receive_filter(struct digipeater_source *src, struct pbuf_t *pb); extern dupecheck_t *digipeater_find_dupecheck(const struct aprx_interface *aif); /* interface.c */ typedef enum { IFTYPE_UNSET, IFTYPE_AX25, IFTYPE_SERIAL, IFTYPE_TCPIP, IFTYPE_AGWPE, IFTYPE_NULL, IFTYPE_APRSIS } iftype_e; struct aprx_interface { iftype_e iftype; int timeout; int16_t ifindex; // Absolute index on this interface int16_t ifgroup; // Group definition on this interface char *callsign; // Callsign of this interface uint8_t ax25call[7]; // AX.25 address field format callsign int aliascount; char **aliases; // Alias callsigns for this interface int8_t subif; // Sub-interface index - for KISS uses uint8_t txrefcount; // Number of digipeaters using this as Tx unsigned tx_ok:1; // This is Tx interface unsigned telemeter_to_is:1; // Telemeter this to APRS-IS unsigned telemeter_to_rf:1; // Telemeter this to this radio port unsigned telemeter_newformat:1; // Telemeter in "new format" int initlength; char *initstring; const void *nax25p; // used on IFTYPE_AX25 #ifdef ENABLE_AGWPE const void *agwpe; // used on IFTYPE_AGWPE #endif struct serialport *tty; // used on IFTYPE_SERIAL, IFTYPE_TCPIP int digisourcecount; struct digipeater_source **digisources; }; extern struct aprx_interface aprsis_interface; extern int top_interfaces_group; extern int all_interfaces_count; extern struct aprx_interface **all_interfaces; extern void interface_init(void); extern int interface_config(struct configfile *cf); extern struct aprx_interface *find_interface_by_callsign(const char *callsign); extern int interface_is_beaconable( const struct aprx_interface *iface ); extern int interface_is_telemetrable(const struct aprx_interface *iface ); extern void interface_receive_ax25( const struct aprx_interface *aif, const char *ifaddress, const int is_aprs, const int ui_pid, const uint8_t *axbuf, const int axaddrlen, const int axlen, const char *tnc2buf, const int tnc2addrlen, const int tnc2len); extern void interface_transmit_ax25(const struct aprx_interface *aif, uint8_t *axaddr, const int axaddrlen, const char *axdata, const int axdatalen); extern void interface_receive_3rdparty(const struct aprx_interface *aif, const char *fromcall, const char *origtocall, const char *gwtype, const char *tnc2data, const int tnc2datalen); extern int interface_transmit_beacon(const struct aprx_interface *aif, const char *src, const char *dest, const char *via, const char *tncbuf, const int tnclen); extern int process_message_to_myself(const struct aprx_interface*const srcif, const struct pbuf_t*const pb); /* pbuf.c */ extern void pbuf_init(void); extern struct pbuf_t *pbuf_get(struct pbuf_t *pb); extern void pbuf_put(struct pbuf_t *pb); extern struct pbuf_t *pbuf_new(const int is_aprs, const int digi_like_aprs, const int tnc2addrlen, const char *tnc2buf, const int tnc2len, const int ax25addrlen, const void *ax25buf, const int ax25len ); /* parse_aprs.c */ extern int parse_aprs(struct pbuf_t*const pb, historydb_t*const historydb); struct aprs_message_t { const char *body; /* message body */ const char *msgid; int body_len; int msgid_len; int is_ack; int is_rej; }; extern int parse_aprs_message(const struct pbuf_t*const pb, struct aprs_message_t*const am); /* filter.c */ struct filter_t; // Forward declarator struct client_t; // Forward declarator struct worker_t; // Forward declarator extern void filter_init(void); extern int filter_parse(struct filter_t **ffp, const char *filt); extern void filter_free(struct filter_t *c); extern int filter_process(struct pbuf_t *pb, struct filter_t *f, historydb_t *historydb); extern void filter_preprocess_dupefilter(struct pbuf_t *pb); extern void filter_postprocess_dupefilter(struct pbuf_t *pb, historydb_t *historydb); extern float filter_lat2rad(float lat); extern float filter_lon2rad(float lon); #ifdef ENABLE_AGWPE /* agwpesocket.c */ extern void *agwpe_addport(const char *hostname, const char *hostport, const char *agwpeport, const struct aprx_interface *interface); extern void agwpe_sendto(const void *_ap, const uint8_t *axaddr, const int axaddrlen, const char *axdata, const int axdatalen); extern int agwpe_prepoll(struct aprxpolls *); extern int agwpe_postpoll(struct aprxpolls *); extern void agwpe_init(void); extern void agwpe_start(void); #endif aprx-2.08.svn593/ViscousDigipeaterTxEffect.png0000644000175000017500000007246112005774520020261 0ustar colincolinPNG  IHDRe$˹sRGBbKGD pHYs  tIME E. IDATxwXe-AoF-V`A vEDVvco1v DQHņ"Eeї'sqq͞ӞS󜙝Q ^Kz @/B]ûwU+-@Ձ^V_'NCl\ݣclΤCgbc<{ngTW/].`NQ1r |QBMXla#˗g>/-=CZMgΩ-5y,*ħI,o,X8nzͥeeYH1 KnU4EM=j,Rظx&xCl-x*]Cm!X;wﵶ8ezl\XBƒ% c[0S!E,nӤmܷo1A.erJB.]puvZqSfVvnܴe<T`fVBHIIi?:)*n./:;ݒ%ltWg'E+'5/QOHμ~9zN{yhv!.m~b0K=Fz}O>+>)TVeL œ':ʒK5"y7l6k+]lVϡ` dOҕkM6xol7ɞœn/Y0c:"zqZz*G^ 88߭;`]g:26x榽MxϿDNۼacnjJȤuuc! z:t יNF=\NKr>RDdٹz,mҤD)i+(G6}1ækw&{ƶ`Lʜ9cj?>5K<bD3SE8S:~O4׋%DEa@Ɖ"&6NL,ܽfrUU޿F|ՐݺB]:R^^~QwY_S΢fdf΢ń f޺_T,#пahij.gȨK !?ǒe x]\h߼uh.{YnwQ˔RWJc^xxގ]{?IUX*vJK;wOqP\kD94"K+j3E?VWW߳c+KF]:8iǮ=.s~a(mȘfq 3Q;0=}ŒE= {m}7-{ƶ`L-Z4Ǥ -Z(.ؼy"Qa33+Ç!?c~AW9orstoެY߲E FNIMYf?&f$%eK4iB;9yeKՈ&99E[ӜWҋݺ$Ky,ӨaC@˗F .{]ۼi'>x&)$b%^,00nFddDth!;@_/*&vCE$S^Y!7o=xHrù@㩫OrL{yor쿗!u}ee7S#Yg޿]g5mipC/ G 2lnh/Ko.\`X?żGa=yO(jiit!yԕxԔQY),|mǺ+/^w%AW;.AԲE]6Js`6mZSw۴iw<sd\1H&%KZ{ɞ&'["Y#Qb/ZJk!U,isˆwv;[k{57z[4cڵkKyB']A6Qۂ1t~ۡ}{㫜A[/&>Mڸy+}%z뗓':>z,*:/Q1G(c_̦/buخ$GmfQGDsQWSvٲ:}igR\B}c䛸#lRҩ)TUinQ.r,}͒+!שk'Ϙ񤴬QxfLJlIɩLYH˅Fb7RRݻuMJNOEw)$9o4P[7c}X\~S <43%qtӤ侖Dal th\ܽ@#͚b=<3+{]`P(?0>/*:fρCϞ=us16)mI8U6z*69%mނ]۷MIT^|ebl4izo\nݾpiw)< gr/'On^4iߟx³evs3Z&)9eܷyڶ^߭;޿onn1onV-scRݪn7RYX^X#E~g~lG~ZP[44KJJ|l޽[5Sta=c=O(5bLG[:g~AAk-ٮ΃gyٳ-[0旱KPװ<bchaϏ* "_\K?p.%%s 6%7|捡9Geba8!<"1ByyuVc  }(vӗ Y/r%f嬍\W/#DFS'O͜>-^s!Ľ ^PPHYayF쀝ܼm[ 4mC/##{$%X xv6IɌ!.M-;K|r[;XXNBRPdžfkgbay;4MS6;p9Ɲb^iۼ׆>/p!AR2I/ڃ6nhؐÆ &DFEoۼ͘:eNӦxA~:6x0{~}UUUFz~A Cnjj=u*Ҭ}`?/޽++/wT !=?߭DEsQKYل-M">45C R˪M?}Dxʖ0t:ya*<6"$*D[oOe݇ul;В%!t2ҼFžeC/^ְ^ZۏzLIKi؃{+!!:C016:~TiiБcBz<~Tyy{BoK<FEEB*, (eeb=()iJmo/^B nBEȄn=rʭ2c=mn%Vrr5V6ټe/Bxǎ[ ĩ3S&Md ce#8h׮!\>xj2BuvԘAH+i/[  wڜ&z@}{+eHD%!ļzfK:M m$.٠fT\IJg|PR .s\̿LHΆȂv'Ka?6!9[lW/0Sc%^Kz %^Kz @/Pzy}CSs8*&~@|}'O͜>-^s!ǎ[ FLJNlmCW99ݾ3}d:PCCҪe@w6x< ef>PzyfϛOC, !|>_KS1$S26" !z׮(--ݝ1\$vRGSgL@~<ˀ%`Fk#Qʹ$67yW!ńv֝ Ӓ ;Ш:,!9;X˾=z)*#!9[lW/0SԞ`1KXE% K /KZH&^Ex r yP^KK*eGɇ*Υ\O^ypz @ͮ$ yB/\0Ya^Kװ̜0u "DBbbGXX:8N` JnC&r٪Փ': ?}ʕ'O͜>-^s!m jW/ORQQ>"$%X xv6IɌ!MӧOl?~^RPXAiղe~Acb˿$hhhs k޷FV\uPUbB45͚|-MMƐ kh`&_ڌO xbܳ'!D_OWBBt3Ȳ X˾Z/+fuxah`q7!d6oަu k0Tim\㊶"?z mnve*@o˕j*Ӧ]DϬM@Q¡.e hr{-Jd,? p U˄l,+`-/Y{ٓq}.ߢ%7cV md)(k u` N;UC ժI*D1vS9IYҢT$-V {/':fTLQ;PUyUnJlOC_a m@Ēθ 6!V-s=SaB@,7\Bzˏp*UF;c(կJe?VN,y buu\B/Ώu0`_nS*c%jg BV,e_r}>Tٶ@&tP;FrkCVGcjF*RV@}9ʝZ}EɚH@= 3*[UYr:\{f bYszPT8YWT07CUyZN'XWֶqb!Rt*ޞ*J4@i'^hEK̳02*,Ueh<UXPud .GǦ7'E5cz Fʊ.&Q/bb'3zc|B"bm?qBxDc ֌5e+Vu{Yᵆr䩙ӧ ={.1^C*Kz{3U*/\J.4j!;[-wBS|֭xv6g3(DFeLi`>R/PYFL2~&ܝaCB 544!Z/(` Q򩿲};XK+%|Uلl}NXH4#\x7U)/]Vs8y˴Py.Xq6"!kij2w#\%I*%'l.}O9ԏ^> [lbڷkG^~JHvwPIvaACt?-jR/W{8t9!dc-|ԙ)&2YQ]_s,R^[ >\ mnveP\sA3ꟊR ))~,5))EQ:Du0ۭrbPblB/f\HhO|j&$z ?\LGd(K@z2pYgBi%`@/_/9*~ p.ȅdT KK?ۛ%_"'6WMX SZ9u1KLMAn~%{.bgJ8vSQu L]^/*X0xeMQawtxzfZBN{뾐J+6!-,k2Às[K%$QM`Wv~jv ɑ.p'vW%$gSOPclPY^$% Y,ԩdֽhJYS5bɈ1PTD+*w#@q J'WU*nJ mWvyT_9@ZZ;e0.dWe/c[z kNQM-K]reOreXPw¹ʏsƲ%@NE!o=$^wJ 8A ?m^ +kcTL( K !(*~CSsɏW~GZpчW/;!O9}ZؽP;[۳C.z|jŋv{Fǎʯ^;yjۦoʗbc5$%'3E?vꤡ1͵Iͦ_/%RPXAiղe~AcP}233 ƍ9+%Pۨ0 ˷vRKS'|-MM&M76k|To3c|B"R_OWBBt3,|G{16m=rrJKK\GSgLsitLLC[t1i]:w;fN} ~`4mE n ANɁ` jDwEX8qV, Y03eQMr,`v)`r+eQJ R aGoܻ'QQ'+l%8wnVtj/G)O,US.Q)v`ŞJq¯TzDb0B`YYٟ'N_X(:<5-](%yd(,n诬F޻WVVp!nXŶ)8kメۡwmGQ.!X]RS.X18(+P?~ lB1B0=# ydGq3?Ԫ IDAT/5-] ’3~JbFSKJj9Jb  Y5%,SS0VdÇ8JW_F5O]jIS>~x^Bz|Z/MtL찡CxлgݰNW[[IL!$$vv` Ɋ':GF5Qصܱk>%o">/҅l/_4kkWZ -Ŏ3Vyw=R_9n|>-<}uS/c#gnkg+L>6h0v̨L:\WG;6>M_rdlԓD..EDF-X ˜_mR~}Vz.K/NO2RT_tKB1zܯ A2&MLtFYҋ#,=M]Mm!Y ~Ȅl5yܸĪ:%5zPgc5^y0FGW]Mid-cRX<1'1%NFfjTSJu+׮Sk>Nʒ;mؠeާ!fԴtnaT3~3gLg٧QFaǂTL2>!Ԅhi;:.N?| c !Qѱ=z1q;wY\UﶷH,|ئMvB:/kymGF]8wz)/_._ ޭ+!yߡ#uw%zZyyy?u,x|`Ff,z]L;okS'Nشu{LߴiBfVorsYB!-nmTĒ?V<v&Xrah?O1uht]DBӤdlY3\ܷܝݶ`dL3ȨpMZӆMIM{>n-ө2 }۵\ITuE͚55[}PD ƭC+!Q11M.-Z(E22*6hf߯EF!a#~3[]MmȠoh 42hSg$+ހ32zRBhdY/c zfkן<&Z7oڻK<= {DDF}]ss,eKJK+z%1@_/<<@_oǮ="v2&Ua؛IZ%%%߹{⏃2^#ʡ1YQ -[Y2yn.N;vq Co @4?ڹgV+,i#%5mﻩo٣0c:UoѢ9fyPGz٢E͛7-6BL>3?|q<:|&7K͚-[`Tuk#ihF_F&ML57BDm@Q ?O^Na3W.["D4)?|(j\zϕ^44{1lȠW,˻p҂bguws=>?P)ESWz_1_KS:2yC1֬yҲ |QT߽{ߪUK~qfV[|Xz忣%=݀s\؋rْ+G j٢Ůmo}sWU0u6imڴf OyŹ 2.A$%Ȟ&'{"Y#Qb/ZJk!U,isbݺ^ji`i kזzOJ _أ0c:4?u&7CW99 JCV^M|qVJ*뗓':>z,*:/Q1G(c_̦9u&$GmfQGDsQWSvٲ:?Uz-^+<%2QX/_̔?P^^~F~ Oo o⎰IMKPSUn2zレsWԺ:׮,))r*-pYOJhFƤdM3)9U) iHFJ*{I) 1趇$&j bLoko fĢ8NN]}Haи8M߹{W9 Gt58F>z,,qyyyfV֝;j˿e=8~ߊ%ԥDmܔ?|(E~YyD. w?+c},;iXa޷pJUޡRWWZϲ9]׬۸q֞=Vzڦuk{_L/]Ę{23Бٳfx[gSe~?ڂӜ:iϖVvwk mKݜJŲDa1c3lW$bhwcǞym٪x1#l1*" EQ1F=cʃX_r_~&*rQ @x? TQQKzY R^*9p1@/n_/ˌ SXX0)I$!$*&~zo % ?Ya9p&P,$$;N_.zPd9A6r^.[zDǰ{/]r䩙ӧ ={.?b!aᏝ :+ ;rވסmkуփݺM#SVV***ߧZZ<&)9tue?{qOB^y3rLoB McCS 3J|m|EC$ chjt)A~O;4 ER]y/(E8HPs xNQG14m^kC^~_Љk)ϤEKNA<b@ q?L4G:;%KBȧOtŪIB BH- ^qӖn]n׮#,^ɺ>2RQQx~φVFBО]b/f,L y;uMm< 'whۆ> 4<~q !M43!];utqe}',R`beNyRCC;oXf,ƍ 2YBHdT)y<ތS"Et6aÆsu _^wu5;vPZOn扆?~"4kA7 /+/w!=_ݢyӉًJzIл !ZE|>!kijJ)UUUEUUyy<^eKX:UU< 0nJ!BBT/^}Ѹ^ζ-YBA.C-͛j4q/iZ{1eͨѱ'={Bt]QZZz%$DGdc'O 9v%XR 'L#Լ}0^䳍Eh Ew@~CX/b=nz)UoTΑð.Ջ6#@XrW<E{.^ߠBxX襒*`%Qr[/mݾE d {!{ڗASֆ^P$?YS8 XҬUS/{q[/eo\s \qSWm ȃ,[~=wP;|TQM#{Ъ%:3]e_C_4▇Rf\8z e5P]MR쬴;JC&cD,.W+^b b0*eMI8_ř&}*4.KUڌpE՗5^UKHX..̟՟X'8l d:\5+G^ߏU-Ezؖ4cP !}P,)/BUjT*|I=xɒ<ʖޛO˷yyQ11L,,'GDP2M~8bY䡓pbd Pzͽ9~ڞ=]#}h_vQ#R%m;W?}>@Xֵ^;yj&_ѐ<&)9X,ƹ}BC`lԮ^سO]DC 544!Z/( 8ljȕFa4f3uq>/Yϸ8֙1$z\K-M">45JB_g7UK-spQ׿'ӽvh{+!!:ݕd լH7ܺgwנ)dڳ6Or6oަu k(R)UNV9GUE^嵦9Uh.]Fǽn^ƕ^'_5SXexW8yh/y^k+* cCL٨؄l,i_I%`-/䂚W4=xq7-V3YFUdX_N֌'T'+dt*m7cMƤNɒvRXv$_3PIYME5[OP+u|Rd+Q}l`rr^r νWV0'3ơ+XemI;obzSmXR VcRG{2ŬJlު[IK`FI *&~i`\򣡩_ykQ/\nfNv/S/{BDlDxlDA𕫁/۵;v\mLJNlm1˕IgVpة7^&&˂B BH- AZ}z;0m233 ƍ9m\[TTTUUA[zYB|&fzyz]%^ 74iB߼SPXج,)9mӽvFii镐*P>`֫WPC; IDATmfx~x^"rɜ~(UtL찡CxлgݰNW[[IL!$$vv` Ɋ':GF5Qصܱk>%o">/҅l/_4kkWZ - [lbڷkWAԣ#c()$;KKTv l ɊPV_̳DU>‡ڌ02fkI8eiԍ?lɓ%C8M.lz2bPT>A>}M$US.!K,Gfj™؏^z @/%u^Kz @/%^Kz @/%̨~xj: DK4cRŞ5`"KbEsw嫜*wzbIS ܽ~qbQ %{ƶ`LɁe<&>MZhpv}S3^&n:+Wgu7efeWM[f9MznAffe_,))!󧓢rrrz-Y^ NMOwuv /X"/?_2Jkԟر3_޼}={jf_y;[K6p1%p}#G^x~@'OLIUX*{JK œ':ʒK5"y7l6k+]lVNϡ` dOҕkM6xol7ɞ\l,Gal t*DxLKXaKguGP΀czyL^F<ܴ״Wc}z7l`Qtvl|!$6>AOG:ب'i\]Z@2;9ZKWzQ.lxē^ޢ_ :$+[|!r!6F8ϞX5YlK63 F`LRۓ%M~q~s2sa_"ddf諪abm׹\N-8҆K׷a}z>aSҵuK= c[0SeϜ1eF}]% RRS1;e|B Ңwt\?EEBc{0cbDski'&w[j*Cmok#{]:R^^~QwY__S΢fdf΢ń f޺_T,#пahijV?ǒeh]x]7ܯ߼uh.DzYʞMJJK+z%1@_/<<@_oǮ=?hƤ*,=YRR'8(c.5CEЙۢϟ?ٱ%.c9J0`dLcqqqȨ{mY_bɢ=RRӶ= c[0Se-c:-Zl޼EnMCb򙙕Ç~E*嫜7]:H7o֬o٢H[OtuD3‰FNriuINN).CQ(gk7x⩫ms?ɒM5l(TUU?ҨaCsWy}7md4dR$[B?mԨu( !d}|7E=phd+#3+zIu8x[WϞHH|}OTt̋/+<Դt~ 5Uբ">!d[jjjs\5h_~JݺN1+z,,qyyyfV֝; PH`|^Tt̞={blSJ7qT mrJۼo顯'“xH_W'iҦޒܺ}'..ay,oaQ'Q7pa^ODݼz)i?BMMkg?>fIRrz͹oڵmj9Q1[wcVZVXMiIUX*v{JKsKgEccX:!$3-3~˲~jCm@,))ٲQXxn]׬ZNmr҅KHܺoLVz.<٣0ֈ1Qnݾsȟf:П}> ?gϞlrØ_B/A<^^K>[U眺]o{oOXtNNTI( BkUBw]FycD"9yԤ=<2w|iƎBBS47/HX٩y<,p=zL,Hr 0l$2?.$`$4w/{Get5eB析Nٟ}_/(,ZB5Hxהۋb)22^{}ά0`9~(B{rOml%$7$p=#'8:ǔٝgw_Z'U(tF欃2nށ!:@˲ZDW/?Vb- P c%^Kz %AfIii(|ߘ)Qo+,TX^+`&$s>{~%qeƎYR^q%8Mss %ccm޴AƆQa `\%|G̈́f`D5#cKG%In^p0%)SedYӣQX\c#^(@MAK@1C@gH ǣC_Kz XL@/%݌ˎO}R=Դ$۷&х̗#/^r}ǏB BW%'U"}GuuGcލ& |Yq3K8=f\Ի1WU  z;w6rߞgV&/YBeEҺԸܣy]\jjkEi-Ъ^7DTTB^H8΋ vHIϫf%V\qC8rd$l9-rRObnzzxׁv{MLjfg*`x!!!zz|%0RjS#U ./_X__߻wotC/ZuRrIFEJ"oB育h` >H pK0 0<}_mNK$SSS33=K7T<ǐfՔwk*01Ri…ڮ i$2ME;#r TB08 %qqqB֭[6m-**4h!ӧǏ~lll˿;3fLmmٳ=<<Ο?y'OWUUEDD̘1#&&&33sʕ|>?>>޽{ƍkhh2dX, NNN۷o裏z\?|HQABPfD|ΥuCmЈT#]4{i+B@ݎƎ~܃o333ׯߍ7XBamm秬y<ޙ3g蒪*++!$%%W{{;'|Bo鏶lBYlWPP@m677 BBbb"!$;;.%P.ȳh"B˗Y!ik$(BBBW]uB/y_7GȿƯ z)Ovv6|>+<==Basvv*޽{*}NNNxHҶ~ĈL_<<<{[[[ӛ_~ %%%LawAח.BimۯL9_#`KutecWjLt0e@p:6lH$>>> 6m֭[>|HZ^^~…>sU\kjjF311qssfV1miiad 2.k׮vBӧo޸ӱi$`wzr OgtزYIe^xᅆ 'O߲+**^z+WP5bXF*|l;.($&&'O$l߾4::Z_`tO;cu?.?jJ!Cߟ{(:uss{YH.(ݻӧOqϞ=Ǿ/a|exY..%lh`۶m׮] c٫Gӕk iii ӣ[nر(##Ν;'OH$qƁ]PH>}Æ ;<} .tAp'ы2@ظqCBCCٳ~a߾}NIIa٫AAA~~~}i1}AIDAThhx`ނ bq\\X,?~eMMX,vwwyΝ;ѣ˖- {LPheeeaa!=gϦׯߜ9s{>ecRiuuH$:u[{tA!ёؔojhkk[l?oee+DcDC5$:aMx(}K.C/p?u{4RRZ$ 7fJ q$O;3) {c,,* ^Dd9Iaݽwho원u};ctm~YX\,)yXHp9cXsZLUƐr#/K~5eLN:dpcS !C?zSo,sn iZ,F^|OQU]ty]:{cգ1G O]7 0XRS[K;:>hnigwtAA2:$'@Kte( BkUBwBף$7/o`XW'")Kgr) vIF0\dيQ~c>M[G͒:eʶL^߾3kz;LFALexPeqΪ{8Y;Z$[iIFBX$8hk${xi_Л*++ }PtJ̰5{j|HKKiה^9rz TZk}ew*jSrIƲF<_]w(G$"iD'FqƮ8f㐒W6ҁv4?laYi~t-n+ήaT} $үN5,)/ ֆaڴi2/n~T|*,,ֶW^Ν;wS?sbbD"aҒ2l0 GGLjJ۷oϟ?ӳG/ٳ޽2"ٍ=m" !))),Fr`ܸqNNN˗/g2Se8^u2*1X'}+Sh3+~7Fr޽q5442D,ohh}wǏ>8q"88XY]6y丸> //>۽{7ikkk@@@qqq\\P(u֦M|}} D)// 5ksO}~#ƪ*b S/UږDx")(pÃX\MqiNd>|IU#BBB_gff3F  6LQ0uK\SÊ i%.LHHʊ)**2d+++gfnIK/4sLǎΎ;v, bq\\X,?~eMMX,vww߲e !$222###88X$ٳw/c*NM|i.]",\Σ 7o4559s&n)*%0YpΎ\ʚ K%w% s8q"Z; |M[[̙3n,)-]ں_x!un#nݾ6pvɥ6͞Q᡺ȩs>ٳÇM0! M]!{_|QF! <~6ӭ {+B*:4)թPhaNeFUqzMݎ-Hpuu677d333ׯ~CO<3N*.KN9|(zixиd*[CX;zyEj<_z!`BHQqɾnjz7*pEÙc?\)KMyU+!y9SUՉKgޅkm4T o:ɔ92^/7nJ\G..5$]*huaevגWM%^on^TI(󴵮kUBw\`&{᩹E?2 XsS,[q ȑSqK&)KFUJH dj$z)8q OݞL]Ɯ" ΂}f@/ZDe5\z`R#x(յu Ba`qx'Х?^Kz @/@_z @/%#zB )&n]$*K|˒ҥ+|񅄅 F{{vĒEetTS=B9Mhfžٙ;v646;:ur{{{=;g;),tמ^bjM,L>uPɔҜ#|͖]S=+L577 ޽/@2_,}6V'}?^hg:t8W!ն^665Bz9844wnbu~"#GoJ1e2צGd2I_xMLۻSm%AssO;f#H17v2]H5bK%z,Xbkk{}jNcSW\S.tz61dQXn^p0ڑ[Fޑ^jUV)2)膡.ԟY뻜Q$7|}|>wk֮NMg[/N"ue_{uj2)fdFݱ1ɲ|3*Mڅ/y]qO?ݼeO(qK[}MxKYRZީCWZ7@GH~UI26(;;c؟N7 QErA%n⠻VnWVQXNt ૡ^Ҩ? IGD|#;':jM~فfCM8 B8h?ԿyGBBHIS=}_vQI {en} S}eԨKe'H$H= Q4 a7 3&БϜY|, %U.fu>ZpnpT*fkRJe̽+˗t~?VQ`ؤSO?p0Î{(ɓ~ H|`qͰZpPHkk[Ӣn]=J !aR4$kUURZUus|e=eևs+^oRɓ'>cI"Rҋ_:|p6??a ;;9 ǁ211!E> a7 3P;r0zb.n&rdline[] array has a KISS frame after * KISS escape decode. The frame begins with KISS command byte, then * AX25 headers and payload, and possibly a CRC-checksum. * Frame length is in S->rdlinelen variable. */ /* KA9Q describes the KISS frame format as follows: http://www.ka9q.net/papers/kiss.html - - - - - - - - - 4. Control of the KISS TNC To distinguish between command and data frames on the host/TNC link, the first byte of each asynchronous frame between host and TNC is a "type" indicator. This type indicator byte is broken into two 4-bit nibbles so that the low-order nibble indicates the command number (given in the table below) and the high-order nibble indicates the port number for that particular command. In systems with only one HDLC port, it is by definition Port 0. In multi-port TNCs, the upper 4 bits of the type indicator byte can specify one of up to sixteen ports. The following commands are defined in frames to the TNC (the "Command" field is in hexadecimal): . . . . . . CMD code 0 is for the data frame, and is only one present coming from TNC to host. - - - - - - - - - SYMEK et al. have defined a way to run CRC inside KISS frames to verify that the KISS-frame itself is correct: http://www.symek.com/g/smack.html http://www.ir3ip.net/iw3fqg/doc/smak.htm SMACK variation recycles the top-most bit of the TNC-id nibble, and thus permits up to 8 TNC ports on line. Top-most bit is always one on SMACK frames. SMACK runs CRC16 over whole KISS frame buffer, including the CMD byte. The CRC-code is thus _different_ from what will be sent out on radio, the latter being CRC-CCITT (see further below): Following CRC16-polynome is used: X^16 + X^15 + X^2 + 1 The CRC-generator is preset to zero. Chosen initialize to zero does mean that after a correct packet with a correct checksum is ran thru this CRC, the output checksum will be zero. - - - - - - - - - Where is FLEXNET specification? */ /* * kissencoder(): If (cmdbyte & 0x80) is set, then this * produces SMACK format frame, otherwise * plain KISS. * */ int kissencoder( void *kissbuf, int kissspace, LineType linetype, const void *pktbuf, int pktlen, int cmdbyte ) { uint8_t *kb = kissbuf; uint8_t *ke = kb + kissspace - 3; const uint8_t *pkt = pktbuf; int i; uint16_t crc16; uint16_t crcflex; crc16 = crc16_table[cmdbyte & 0xFF]; crcflex = 0xff00 ^ crc_flex_table[(~cmdbyte) & 0xff]; /* Expect the KISS buffer to be at least ... 8 bytes.. */ *kb++ = KISS_FEND; *kb++ = cmdbyte; for (i = 0; i < pktlen && kb < ke; ++i, ++pkt) { // Calc CRCs while encoding data.. int b = *pkt; crc16 = ((crc16 >> 8) & 0xff) ^ crc16_table[(crc16 ^ b) & 0xFF]; crcflex = (crcflex << 8) ^ crc_flex_table[((crcflex >> 8) ^ b) & 0xff]; if (b == KISS_FEND) { *kb++ = KISS_FESC; *kb++ = KISS_TFEND; } else { *kb++ = b; if (b == KISS_FESC) *kb++ = KISS_TFESC; } } /* If caller is asking for SMACK format frame, then store calculated CRC on frame. - CRC-bytes must be KISS escaped! */ /* If caller is asking for SMACK/FLEXNET format frame, then store calculated CRC on frame. - CRC-bytes must be KISS escaped! */ if (linetype == LINETYPE_KISSSMACK || linetype == LINETYPE_KISSFLEXNET) { int crc, b; if (linetype == LINETYPE_KISSSMACK) { crc = crc16; } else if (linetype == LINETYPE_KISSFLEXNET) { crc = crcflex; } else { // Silence compiler warning, this branch is never reached.. crc = 0; } b = crc & 0xFF; /* low crc byte */ if (b == KISS_FEND) { if (kb < ke) *kb++ = KISS_FESC; if (kb < ke) *kb++ = KISS_TFEND; } else { if (kb < ke) *kb++ = b; if (b == KISS_FESC && kb < ke) *kb++ = KISS_TFESC; } b = (crc >> 8) & 0xFF; /* high crc byte */ if (b == KISS_FEND) { if (kb < ke) *kb++ = KISS_FESC; if (kb < ke) *kb++ = KISS_TFEND; } else { if (kb < ke) *kb++ = b; if (b == KISS_FESC && kb < ke) *kb++ = KISS_TFESC; } } if (kb < ke) { *kb++ = KISS_FEND; return (kb - (uint8_t *) (kissbuf)); } else { /* Didn't fit in... */ return 0; } } static int kissprocess(struct serialport *S) { int i; int cmdbyte = S->rdline[0]; int tncid = (cmdbyte >> 4) & 0x0F; /* -- * C0 00 * 82 A0 B4 9A 88 A4 60 * 9E 90 64 90 A0 9C 72 * 9E 90 64 A4 88 A6 E0 * A4 8C 9E 9C 98 B2 61 * 03 F0 * 21 36 30 32 39 2E 35 30 4E 2F 30 32 35 30 35 2E 34 33 45 3E 20 47 43 53 2D 38 30 31 20 * C0 * -- */ /* printf("kissprocess() cmdbyte=%02X len=%d ",cmdbyte,S->rdlinelen); */ /* Ok, cmdbyte tells us something, and we should ignore the frame if we don't know it... */ if ((cmdbyte & 0x0F) != 0) { /* There should NEVER be any other value in the CMD bits than 0 coming from TNC to host! */ /* printf(" ..bad CMD byte\n"); */ if (debug) { printf("%ld\tTTY %s: Bad CMD byte on KISS frame: ", tick.tv_sec, S->ttyname); hexdumpfp(stdout, S->rdline, S->rdlinelen, 1); printf("\n"); } erlang_add(S->ttycallsign[tncid], ERLANG_DROP, S->rdlinelen, 1); /* Account one packet */ return -1; } if (S->linetype == LINETYPE_KISS && (cmdbyte & 0x20)) { // Huh? Perhaps a FLEXNET packet? int crcflex = calc_crc_flex(S->rdline, S->rdlinelen); if (crcflex == 0x7070) { if (debug) printf("ALERT: Looks like received KISS frame is a FLEXNET with CRC!\n"); S->linetype = LINETYPE_KISSFLEXNET; } } if (S->linetype == LINETYPE_KISS && (cmdbyte & 0x80)) { // Huh? Perhaps a SMACK packet? int smack_ok = check_crc_16(S->rdline, S->rdlinelen); if (smack_ok == 0) { if (debug) printf("ALERT: Looks like received KISS frame is a SMACK with CRC!\n"); S->linetype = LINETYPE_KISSSMACK; } } /* Are we expecting FLEXNET KISS ? */ if (S->linetype == LINETYPE_KISSFLEXNET && (cmdbyte & 0x20)) { int crc; tncid &= ~0x20; // FlexNet puts 0x20 as indication of CRC presence.. if (S->ttycallsign[tncid] == NULL) { /* D'OH! received packet on multiplexer tncid without callsign definition! We discard this packet! */ if (debug > 0) { printf("%ld\tTTY %s: Bad TNCID on CMD byte on a KISS frame: %02x No interface configured for it! ", tick.tv_sec, S->ttyname, cmdbyte); hexdumpfp(stdout, S->rdline, S->rdlinelen, 1); printf("\n"); } erlang_add(S->ttycallsign[tncid], ERLANG_DROP, S->rdlinelen, 1); /* Account one packet */ return -1; } crc = calc_crc_flex(S->rdline, S->rdlinelen); if (crc != 0x7070) { if (debug) { printf("%ld\tTTY %s tncid %d: Received FLEXNET frame with invalid CRC %04x: ", tick.tv_sec, S->ttyname, tncid, crc); hexdumpfp(stdout, S->rdline, S->rdlinelen, 1); printf("\n"); } erlang_add(S->ttycallsign[tncid], ERLANG_DROP, S->rdlinelen, 1); // Account one packet return -1; // The CRC was invalid.. } S->rdlinelen -= 2; // remove 2 bytes! } /* Are we excepting BPQ "CRC" (XOR-sum of data) */ if (S->linetype == LINETYPE_KISSBPQCRC) { /* TODO: in what conditions the "CRC" is calculated and when not ? */ int xorsum = 0; if (S->ttycallsign[tncid] == NULL) { /* D'OH! received packet on multiplexer tncid without callsign definition! We discard this packet! */ if (debug > 0) { printf("%ld\tTTY %s: Bad TNCID on CMD byte on a KISS frame: %02x No interface configured for it! ", tick.tv_sec, S->ttyname, cmdbyte); hexdumpfp(stdout, S->rdline, S->rdlinelen, 1); printf("\n"); } erlang_add(S->ttycallsign[tncid], ERLANG_DROP, S->rdlinelen, 1); /* Account one packet */ return -1; } for (i = 1; i < S->rdlinelen; ++i) xorsum ^= S->rdline[i]; xorsum &= 0xFF; if (xorsum != 0) { if (debug) { printf("%ld\tTTY %s tncid %d: Received bad BPQCRC: %02x: ", tick.tv_sec, S->ttyname, tncid, xorsum); hexdumpfp(stdout, S->rdline, S->rdlinelen, 1); printf("\n"); } erlang_add(S->ttycallsign[tncid], ERLANG_DROP, S->rdlinelen, 1); /* Account one packet */ return -1; } S->rdlinelen -= 1; /* remove the sum-byte from tail */ if (debug > 2) printf("%ld\tTTY %s tncid %d: Received OK BPQCRC frame\n", tick.tv_sec, S->ttyname, tncid); } /* Are we expecting SMACK ? */ if (S->linetype == LINETYPE_KISSSMACK) { tncid &= 0x07; /* Chop off top bit */ if (S->ttycallsign[tncid] == NULL) { /* D'OH! received packet on multiplexer tncid without callsign definition! We discard this packet! */ if (debug > 0) { printf("%ld\tTTY %s: Bad TNCID on CMD byte on a KISS frame: %02x No interface configured for it! ", tick.tv_sec, S->ttyname, cmdbyte); hexdumpfp(stdout, S->rdline, S->rdlinelen, 1); printf("\n"); } erlang_add(S->ttycallsign[tncid], ERLANG_DROP, S->rdlinelen, 1); /* Account one packet */ return -1; } if ((cmdbyte & 0x8F) == 0x80) { /* SMACK data frame */ if (debug > 3) printf("%ld\tTTY %s tncid %d: Received SMACK frame\n", tick.tv_sec, S->ttyname, tncid); if (!(S->smack_subids & (1 << tncid))) { if (debug) printf("%ld\t... marking received SMACK\n", tick.tv_sec); } S->smack_subids |= (1 << tncid); /* It is SMACK frame -- KISS with CRC16 at the tail. Now we ignore the TNC-id number field. Verify the CRC.. */ // Whole buffer including CMD-byte! if (check_crc_16(S->rdline, S->rdlinelen) != 0) { if (debug) { printf("%ld\tTTY %s tncid %d: Received SMACK frame with invalid CRC: ", tick.tv_sec, S->ttyname, tncid); hexdumpfp(stdout, S->rdline, S->rdlinelen, 1); printf("\n"); } erlang_add(S->ttycallsign[tncid], ERLANG_DROP, S->rdlinelen, 1); // Account one packet return -1; /* The CRC was invalid.. */ } S->rdlinelen -= 2; /* Chop off the two CRC bytes */ } else if ((cmdbyte & 0x8F) == 0x00) { /* * Expecting SMACK data, but got plain KISS data. * Send a flow-rate limited probes to TNC to enable * SMACK -- lets use 30 minutes window... */ S->smack_subids &= ~(1 << tncid); // Turn off the SMACK mode indication bit.. if (debug > 2) printf("%ld\tTTY %s tncid %d: Expected SMACK, got KISS.\n", tick.tv_sec, S->ttyname, tncid); if (timecmp(S->smack_probe[tncid], tick.tv_sec) < 0) { uint8_t probe[4]; uint8_t kissbuf[12]; int kisslen; probe[0] = cmdbyte | 0x80; /* Make it into SMACK */ probe[1] = 0; /* Convert the probe packet to KISS frame */ kisslen = kissencoder( kissbuf, sizeof(kissbuf), S->linetype, &(probe[1]), 1, probe[0] ); /* Send probe message.. */ if (S->wrlen + kisslen < sizeof(S->wrbuf)) { /* There is enough space in writebuf! */ memcpy(S->wrbuf + S->wrlen, kissbuf, kisslen); S->wrlen += kisslen; /* Flush it out.. and if not successfull, poll(2) will take care of it soon enough.. */ ttyreader_linewrite(S); S->smack_probe[tncid] = tick.tv_sec + 1800; /* 30 minutes */ if (debug) printf("%ld\tTTY %s tncid %d: Sending SMACK activation probe packet\n", tick.tv_sec, S->ttyname, tncid); } /* Else no space to write ? Huh... */ } } else { // Else... there should be no other kind data frames if (debug) { printf("%ld\tTTY %s: Bad CMD byte on expected SMACK frame: %02x, len=%d: ", tick.tv_sec, S->ttyname, cmdbyte, S->rdlinelen); hexdumpfp(stdout, S->rdline, S->rdlinelen, 1); printf("\n"); } erlang_add(S->ttycallsign[tncid], ERLANG_DROP, S->rdlinelen, 1); /* Account one packet */ return -1; } } /* Are we expecting Basic KISS ? */ if (S->linetype == LINETYPE_KISS) { if (S->ttycallsign[tncid] == NULL) { /* D'OH! received packet on multiplexer tncid without callsign definition! We discard this packet! */ if (debug > 0) { printf("%ld\tTTY %s: Bad TNCID on CMD byte on a KISS frame: %02x No interface configured for it! ", tick.tv_sec, S->ttyname, cmdbyte); hexdumpfp(stdout, S->rdline, S->rdlinelen, 1); printf("\n"); } erlang_add(S->ttycallsign[tncid], ERLANG_DROP, S->rdlinelen, 1); /* Account one packet */ return -1; } } if (S->rdlinelen < 17) { /* 7+7+2 bytes of minimal AX.25 frame + 1 for KISS CMD byte */ /* Too short frame.. */ /* printf(" ..too short a frame for anything\n"); */ erlang_add(S->ttycallsign[tncid], ERLANG_DROP, S->rdlinelen, 1); /* Account one packet */ return -1; } /* Valid AX.25 HDLC frame byte sequence is now at S->rdline[1..S->rdlinelen-1] */ /* Send the frame to APRS-IS, return 1 if valid AX.25 UI message, does not validate against valid APRS message rules... (TODO: it could do that too) */ // The AX25_TO_TNC2 does validate the AX.25 packet, // converts it to "TNC2 monitor format" and sends it to // Rx-IGate functionality. Returns non-zero only when // AX.25 header is OK, and packet is sane. erlang_add(S->ttycallsign[tncid], ERLANG_RX, S->rdlinelen, 1); /* Account one packet */ if (ax25_to_tnc2(S->interface[tncid], S->ttycallsign[tncid], tncid, cmdbyte, S->rdline + 1, S->rdlinelen - 1)) { // The packet is valid per AX.25 header bit rules. #ifdef PF_AX25 /* PF_AX25 exists -- highly likely a Linux system ! */ /* Send the frame without cmdbyte to internal AX.25 network */ if (S->netax25[tncid] != NULL) netax25_sendax25(S->netax25[tncid], S->rdline + 1, S->rdlinelen - 1); #endif } else { // The packet is not valid per AX.25 header bit rules erlang_add(S->ttycallsign[tncid], ERLANG_DROP, S->rdlinelen, 1); /* Account one packet */ if (aprxlogfile) { // NOT replaced with aprxlog() -- because this is a bit more complicated.. FILE *fp = fopen(aprxlogfile, "a"); if (fp) { char timebuf[60]; printtime(timebuf, sizeof(timebuf)); setlinebuf(fp); fprintf(fp, "%s ax25_to_tnc2(%s,len=%d) rejected the message: ", timebuf, S->ttycallsign[tncid], S->rdlinelen-1); hexdumpfp(fp, S->rdline, S->rdlinelen, 1); fprintf(fp, "\n"); fclose(fp); } } } return 0; } /* * ttyreader_pullkiss() -- pull KISS (or KISS+CRC) frame, and call KISS processor */ int kiss_pullkiss(struct serialport *S) { /* printf("ttyreader_pullkiss() rdlen=%d rdcursor=%d, state=%d\n", S->rdlen, S->rdcursor, S->kissstate); fflush(stdout); */ /* At incoming call there is at least one byte in between S->rdcursor and S->rdlen */ /* Phases: kissstate == 0: hunt for KISS_FEND, discard everything before it. kissstate != 0: reading has globbed up preceding KISS_FENDs ("HDLC flags") and the cursor is in front of a frame */ /* There are TNCs that use "shared flags" - only one FEND in between data frames. */ if (S->kissstate == KISSSTATE_SYNCHUNT) { /* Hunt for KISS_FEND, discard everything until then! */ int c; for (;;) { c = ttyreader_getc(S); if (c < 0) return c; /* Out of buffer, stay in state, return latter when there is some refill */ if (c == KISS_FEND) /* Found the sync-byte ! change state! */ break; } S->kissstate = KISSSTATE_COLLECTING; } if (S->kissstate != KISSSTATE_SYNCHUNT) { /* Normal processing mode */ int c; for (;;) { c = ttyreader_getc(S); if (c < 0) return c; /* Out of input stream, exit now, come back latter.. */ /* printf(" %02X", c); if (c == KISS_FEND) { printf("\n");fflush(stdout); } */ if (c == KISS_FEND) { /* Found end-of-frame character -- or possibly beginning.. This never exists in datastream except as itself. */ if (S->rdlinelen > 0) { /* Non-zero sized frame Process it away ! */ kissprocess(S); S->kissstate = KISSSTATE_COLLECTING; S->rdlinelen = 0; } /* rdlinelen == 0 because we are receiving consequtive FENDs, or just processed our previous frame. Treat them the same: discard this byte. */ continue; } if (S->kissstate == KISSSTATE_KISSFESC) { /* We have some char, state switches to normal collecting */ S->kissstate = KISSSTATE_COLLECTING; if (c == KISS_TFEND) c = KISS_FEND; else if (c == KISS_TFESC) c = KISS_FESC; else continue; /* Accepted chars after KISS_FESC are only TFEND and TFESC. Others must be discarded. */ } else { /* Normal collection mode */ if (c == KISS_FESC) { S->kissstate = KISSSTATE_KISSFESC; continue; /* Back to top of the loop and continue.. */ } } if (S->rdlinelen >= (sizeof(S->rdline) - 3)) { /* Too long ! Way too long ! */ S->kissstate = KISSSTATE_SYNCHUNT; /* Sigh.. discard it. */ S->rdlinelen = 0; if (debug) { printf("%ld\tTTY %s: Too long frame to be KISS: ", tick.tv_sec, S->ttyname); hexdumpfp(stdout, S->rdline, S->rdlinelen, 1); printf("\n"); } continue; } /* Put it on record store: */ S->rdline[S->rdlinelen++] = c; } /* .. for(..) loop of data collecting */ } /* .. normal consumption mode ... */ return 0; } /* * kiss_kisswrite() -- write out buffered data */ void kiss_kisswrite(struct serialport *S, const int tncid, const uint8_t *ax25raw, const int ax25rawlen) { int i, len, ssid; uint8_t kissbuf[2300]; if (debug) { printf("kiss_kisswrite(->%s, axlen=%d)", S->ttycallsign[tncid], ax25rawlen); } if (S->fd < 0) { if (debug) printf("NOTE: Write to non-open serial port discarded."); return; } if ((S->linetype != LINETYPE_KISS) && (S->linetype != LINETYPE_KISSSMACK) && (S->linetype != LINETYPE_KISSFLEXNET) && (S->linetype != LINETYPE_KISSBPQCRC)) { if (debug) printf("WARNING: WRITING KISS FRAMES ON SERIAL/TCP LINE OF NO KISS TYPE IS UNSUPPORTED!\n"); return; } if ((S->wrlen == 0) || (S->wrlen > 0 && S->wrcursor >= S->wrlen)) { S->wrlen = S->wrcursor = 0; } else { /* There is some data in between wrcursor and wrlen */ len = S->wrlen - S->wrcursor; if (len > 0) { i = write(S->fd, S->wrbuf + S->wrcursor, len); } else i = 0; if (i > 0) { /* wrote something */ S->wrcursor += i; len = S->wrlen - S->wrcursor; if (len == 0) { S->wrcursor = S->wrlen = 0; /* wrote all ! */ } else { /* compact the buffer a bit */ memcpy(S->wrbuf, S->wrbuf + S->wrcursor, len); S->wrcursor = 0; S->wrlen = len; } } } ssid = (tncid << 4) | ((S->linetype == LINETYPE_KISSSMACK) ? 0x80 : 0x00); if (S->linetype == LINETYPE_KISSFLEXNET) ssid |= 0x20; // CRC presence len = kissencoder( kissbuf, sizeof(kissbuf), S->linetype, ax25raw, ax25rawlen, ssid ); if (debug>2) { printf("kiss-encoded: "); hexdumpfp(stdout, kissbuf, len, 1); printf("\n"); } // Will the KISS encoded frame fit in the link buffer? if ((S->wrlen + len) < sizeof(S->wrbuf)) { memcpy(S->wrbuf + S->wrlen, kissbuf, len); S->wrlen += len; erlang_add(S->ttycallsign[tncid], ERLANG_TX, ax25rawlen, 1); if (debug) printf(" .. put %d bytes of KISS frame on IO buffer\n",len); } else { // No fit! if (debug) printf(" .. %d bytes of KISS frame did not fit on IO buffer\n",len); return; } // Try to write it immediately len = S->wrlen - S->wrcursor; if (len > 0) i = write(S->fd, S->wrbuf + S->wrcursor, len); else i = 0; if (i > 0) { /* wrote something */ S->wrcursor += i; len = S->wrlen - S->wrcursor; /* all done? */ if (len == 0) { S->wrcursor = S->wrlen = 0; /* wrote all ! */ } else { /* compact the buffer a bit */ memcpy(S->wrbuf, S->wrbuf + S->wrcursor, len); S->wrcursor = 0; S->wrlen = len; } } } void kiss_poll(struct serialport *S) { uint8_t probe[1]; uint8_t kissbuf[12]; int kisslen; int tncid; for (tncid = 0; tncid < 16; ++tncid) { if (S->interface[tncid] == NULL) { // No sub-interface here.. continue; } probe[0] = 0x0E | (tncid << 4); /* Convert the probe packet to KISS frame */ kisslen = kissencoder( kissbuf, sizeof(kissbuf), S->linetype, &(probe[0]), 0, probe[0] ); /* Send probe message.. */ if (S->wrlen + kisslen < sizeof(S->wrbuf)) { /* There is enough space in writebuf! */ memcpy(S->wrbuf + S->wrlen, kissbuf, kisslen); S->wrlen += kisslen; /* Flush it out.. and if not successfull, poll(2) will take care of it soon enough.. */ ttyreader_linewrite(S); if (debug) printf("%ld.%06d\tTTY %s tncid %d: Sending KISS POLL\n", (long)tick.tv_sec, (int)tick.tv_usec, S->ttyname, tncid); } } } aprx-2.08.svn593/dupecheck.c0000644000175000017500000002742212305424775014574 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" /* * Some parts of this code are copied from: */ /* * aprsc * * (c) Heikki Hannikainen, OH7LZB * * This program is licensed under the BSD license, which can be found * in the file LICENSE. * */ /* * dupecheck.c: the dupe-checkers */ static int dupecheck_cellgauge; static int dupecheckers_count; static dupecheck_t **dupecheckers; #ifndef _FOR_VALGRIND_ cellarena_t *dupecheck_cells; #endif const int duperecord_size = sizeof(struct dupe_record_t); const int duperecord_align = __alignof__(struct dupe_record_t); /* * The cellmalloc does not need internal MUTEX, it is being used in single thread.. */ void dupecheck_init(void) { #ifndef _FOR_VALGRIND_ dupecheck_cells = cellinit( "dupecheck", duperecord_size, duperecord_align, CELLMALLOC_POLICY_LIFO | CELLMALLOC_POLICY_NOMUTEX, 4 /* 4 kB at the time */, 0 /* minfree */); #endif } /* * dupecheck_new() creates a new instance of dupechecker * */ dupecheck_t *dupecheck_new(const int storetime) { dupecheck_t *dp = calloc(1, sizeof(dupecheck_t)); ++dupecheckers_count; dupecheckers = realloc(dupecheckers, sizeof(dupecheck_t *) * dupecheckers_count); dupecheckers[ dupecheckers_count -1 ] = dp; dp->storetime = storetime; return dp; } static dupe_record_t *dupecheck_db_alloc(int alen, int pktlen) { dupe_record_t *dp; #ifndef _FOR_VALGRIND_ // if (debug) printf("DUPECHECK db alloc(alen=%d,dlen=%d) %s", // alen,pktlen, dupecheck_free ? "FreeChain":"CellMalloc"); dp = cellmalloc(dupecheck_cells); // if (debug) printf(" dp=%p\n",dp); if (dp == NULL) return NULL; // cellmalloc() block may need separate pktbuf memset(dp, 0, sizeof(*dp)); dp->packet = dp->packetbuf; if (pktlen > sizeof(dp->packetbuf)) dp->packet = malloc(pktlen+1); #else // directly malloced block is fine as is dp = calloc(1, pktlen + sizeof(*dp)); dp->packet = dp->packetbuf; // always suitable size #endif dp->alen = alen; dp->plen = pktlen; ++dupecheck_cellgauge; dupecheck_get(dp); // increment refcount // if(debug)printf("DUPECHECK db alloc() returning dp=%p\n",dp); return dp; } static void dupecheck_db_free(dupe_record_t *dp) { if (dp->pbuf != NULL) { // If a pbuf is referred, release it pbuf_put(dp->pbuf); // decrements refcount - and frees at zero. dp->pbuf = NULL; } #ifndef _FOR_VALGRIND_ if (dp->packet != dp->packetbuf) free(dp->packet); cellfree(dupecheck_cells, dp); #else free(dp); #endif --dupecheck_cellgauge; } // Increment refcount dupe_record_t *dupecheck_get(dupe_record_t *dp) { dp->refcount += 1; return dp; } // Decrement refcount, when zero, call free void dupecheck_put(dupe_record_t *dp) { dp->refcount -= 1; if (dp->refcount <= 0) { dupecheck_db_free(dp); } } /* The dupecheck_cleanup() is for regular database cleanups, * Call this about once a minute. * * Note: entry validity is possibly shorter time than the cleanup * invocation interval! */ static void dupecheck_cleanup(void) { dupe_record_t *dp, **dpp; int cleancount = 0, i, d; // All dupecheckers.. for (d = 0; d < dupecheckers_count; ++d) { // Within this dupechecker... struct dupecheck_t *dpc = dupecheckers[d]; for (i = 0; i < DUPECHECK_DB_SIZE; ++i) { dpp = & (dpc->dupecheck_db[i]); while (( dp = *dpp )) { if ((dp->t_exp - tick.tv_sec) < 0) { /* Old.. discard. */ *dpp = dp->next; dp->next = NULL; dupecheck_put(dp); ++cleancount; continue; } /* No expiry, just advance the pointer */ dpp = &dp->next; } } } // hlog( LOG_DEBUG, "dupecheck_cleanup() removed %d entries, count now %ld", // cleancount, dupecheck_cellgauge ); } /* * Check a single packet for duplicates in APRS sense * The addr/alen must be in TNC2 monitor format, data/dlen * are expected to be APRS payload as well. */ dupe_record_t *dupecheck_aprs(dupecheck_t *dpc, const char *addr, const int alen, const char *data, const int dlen) { /* check a single packet */ // pb->flags |= F_DUPE; /* this is a duplicate! */ int i; int addrlen; // length of the address part int datalen; // length of the payload uint32_t hash, idx; dupe_record_t **dpp, *dp; // 1) collect canonic rep of the address (SRC,DEST, no VIAs) i = 1; for (addrlen = 0; addrlen < alen; ++ addrlen) { const char c = addr[addrlen]; if (c == 0 || c == ',' || c == ':') { break; } if (c == '-' && i) { i = 0; } } // code to prevent segmentation fault if (addrlen > 18) { if (debug>1) printf(" addrlen=\"%d\" > 18, discard packet\n",addrlen); return NULL; } // Canonic tail has no SPACEs in data portion! // TODO: how to treat 0 bytes ??? datalen = dlen; while (datalen > 0 && data[datalen-1] == ' ') --datalen; // there are no 3rd-party frames in APRS-IS ... // 2) calculate checksum (from disjoint memory areas) hash = keyhash(addr, addrlen, 0); hash = keyhash(data, datalen, hash); idx = hash; // 3) lookup if same checksum is in some hash bucket chain // 3b) compare packet... // 3b1) flag as F_DUPE if so // DUPECHECK_DB_SIZE == 16 -> 4 bits index idx ^= (idx >> 16); /* fold the hash bits.. */ idx ^= (idx >> 8); /* fold the hash bits.. */ idx ^= (idx >> 4); /* fold the hash bits.. */ i = idx % DUPECHECK_DB_SIZE; dpp = &(dpc->dupecheck_db[i]); while (*dpp) { dp = *dpp; if ((dp->t_exp - tick.tv_sec) < 0) { // Old ones are discarded when seen *dpp = dp->next; dp->next = NULL; dupecheck_put(dp); continue; } if (dp->hash == hash) { // HASH match! And not too old! if (dp->alen == addrlen && dp->plen == datalen && memcmp(addr, dp->addresses, addrlen) == 0 && memcmp(data, dp->packet, datalen) == 0) { // PACKET MATCH! dp->seen += 1; return dp; } // no packet match.. check next } dpp = &dp->next; } // dpp points to pointer at the tail of the chain // 4) Add comparison copy of non-dupe into dupe-db dp = dupecheck_db_alloc(addrlen, datalen); if (dp == NULL) return NULL; // alloc error! *dpp = dp; // Put it on tail of existing chain memcpy(dp->addresses, addr, addrlen); memcpy(dp->packet, data, datalen); dp->seen = 1; // First observation gets number 1 dp->hash = hash; dp->t = tick.tv_sec; dp->t_exp = tick.tv_sec + dpc->storetime; return NULL; } /* * dupecheck_pbuf() returns pointer to dupe record, if pbuf is * a duplicate. Otherwise it return a NULL. */ dupe_record_t *dupecheck_pbuf(dupecheck_t *dpc, struct pbuf_t *pb, const int viscous_delay) { int i; uint32_t hash, idx; dupe_record_t **dpp, *dp; const char *addr = pb->data; int alen = pb->dstcall_end - addr; const char *dataend = pb->data + pb->packet_len; const char *data = pb->info_start; int dlen = dataend - data; int addrlen = alen; int datalen = dlen; char *p; /* if (debug && pb->is_aprs) { printf("dupecheck[1] addr='"); fwrite(addr, alen, 1, stdout); printf("' data='"); fwrite(data, dlen, 1, stdout); printf("'\n"); } */ // Canonic tail has no SPACEs in data portion! // TODO: how to treat 0 bytes ??? if (!pb->is_aprs) { // data and dlen are raw AX.25 section pointers data = (const char*) pb->ax25data; datalen = pb->ax25datalen; } else { // Do with APRS rules for (;;) { // 1) collect canonic rep of the address i = 1; for (addrlen = 0; addrlen < alen; ++ addrlen) { const char c = addr[addrlen]; if (c == 0 || c == ',' || c == ':') { break; } if (c == '-' && i) { i = 0; } } while (datalen > 0 && data[datalen-1] == ' ') --datalen; if (data[0] == '}') { // 3rd party frame! addr = data+1; p = memchr(addr,':',datalen-1); if (p == NULL) break; // Invalid 3rd party frame, no ":" in it alen = p - addr; data = p+1; datalen = dataend - data; /* if (debug && pb->is_aprs) { printf("dupecheck[2] 3rd-party: addr='"); fwrite(addr, alen, 1, stdout); printf("' data='"); fwrite(data, datalen, 1, stdout); printf("'\n"); } */ continue; // repeat the processing! } break; // No repeat necessary in general case } } // 2) calculate checksum (from disjoint memory areas) /* if (debug && pb->is_aprs) { printf("dupecheck[3] addr='"); fwrite(addr, addrlen, 1, stdout); printf("' data='"); fwrite(data, datalen, 1, stdout); printf("'\n"); } */ hash = keyhash(addr, addrlen, 0); hash = keyhash(data, datalen, hash); idx = hash; /* if (debug>1) { printf("DUPECHECK: Addr='"); fwrite(addr, 1, addrlen, stdout); printf("' Data='"); fwrite(data, 1, datalen, stdout); printf("' hash=%x\n", hash); } */ // 3) lookup if same checksum is in some hash bucket chain // 3b) compare packet... // 3b1) flag as F_DUPE if so // DUPECHECK_DB_SIZE == 16 -> 4 bits index idx ^= (idx >> 16); /* fold the hash bits.. */ idx ^= (idx >> 8); /* fold the hash bits.. */ idx ^= (idx >> 4); /* fold the hash bits.. */ i = idx % DUPECHECK_DB_SIZE; dpp = &(dpc->dupecheck_db[i]); while (*dpp) { dp = *dpp; if ((dp->t_exp - tick.tv_sec) < 0) { // Old ones are discarded when seen *dpp = dp->next; dp->next = NULL; dupecheck_put(dp); continue; } if (dp->hash == hash) { // HASH match! And not too old! if (dp->alen == addrlen && dp->plen == datalen && memcmp(addr, dp->addresses, addrlen) == 0 && memcmp(data, dp->packet, datalen) == 0) { // PACKET MATCH! if (viscous_delay > 0) dp->delayed_seen += 1; else dp->seen += 1; return dp; } // no packet match.. check next } dpp = &dp->next; } // dpp points to pointer at the tail of the chain // 4) Add comparison copy of non-dupe into dupe-db dp = dupecheck_db_alloc(addrlen, datalen); if (dp == NULL) { if (debug) printf("DUPECHECK ALLOC ERROR!\n"); return NULL; // alloc error! } *dpp = dp; // Put it on tail of existing chain memcpy(dp->addresses, addr, addrlen); memcpy(dp->packet, data, datalen); dp->pbuf = pbuf_get(pb); // increments refcount if (viscous_delay > 0) { // First observation gets number 1 dp->seen = 0; dp->delayed_seen = 1; dp->pbuf = pb; } else { dp->seen = 1; dp->delayed_seen = 0; } dp->hash = hash; dp->t = tick.tv_sec; dp->t_exp = tick.tv_sec + dpc->storetime; return dp; } /* * dupechecker aprx poll integration, timed tasks control * */ static struct timeval dupecheck_cleanup_nexttime; static void dupecheck_resettime(void *arg) { struct timeval *tv = (struct timeval *)arg; *tv = tick; } int dupecheck_prepoll(struct aprxpolls *app) { if (time_reset) { dupecheck_resettime(&dupecheck_cleanup_nexttime); } if (dupecheck_cleanup_nexttime.tv_sec == 0) dupecheck_cleanup_nexttime = tick; if (tv_timercmp(&dupecheck_cleanup_nexttime, &app->next_timeout) > 0) app->next_timeout = dupecheck_cleanup_nexttime; return 0; /* No poll descriptors, only time.. */ } int dupecheck_postpoll(struct aprxpolls *app) { if (tv_timercmp(&dupecheck_cleanup_nexttime, &tick) > 0) return 0; /* Too early.. */ tv_timeradd_seconds( &dupecheck_cleanup_nexttime, &tick, 30 ); // tick every 30 second or so dupecheck_cleanup(); return 0; } aprx-2.08.svn593/valgrind.c0000644000175000017500000000514312305424772014440 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #ifdef _FOR_VALGRIND_ #include "aprx.h" /* * High-efficiency algorithms used by libc cause terrible complaints * from valgrind.. * * These naive single char at the time things don't go reading into * uninitialized areas.. * */ int memcmp(const void *p1, const void *p2, size_t n) { const char *s1 = p1; const char *s2 = p2; for( ; n > 0 && *s1 == *s2 ; ++s1, ++s2, --n ) ; if (n == 0) return 0; return (*s1 - *s2); } void *memcpy(void *dest, const void *src, size_t n) { char *p = dest; const char *s = src; for ( ; n > 0; --n ) { *p++ = *s++; } return dest; } size_t strlen(const char *p) { size_t i; for ( i = 0; *p != 0; ++p, ++i ) ; return i; } char *strdup(const char *s) { int len = strlen(s)+1; char *p = malloc(len); memcpy(p, s, len); return p; } int strcmp(const char *s1, const char *s2) { for( ; *s1 && *s2 && *s1 == *s2 ; ++s1, ++s2 ) ; if (*s1 == 0 && *s2 == 0) return 0; if (*s1 == 0) return -1; if (*s2 == 0) return 1; return (*s1 - *s2); } int strncmp(const char *s1, const char *s2, size_t n) { for( ; n > 0 && *s1 && *s2 && *s1 == *s2 ; ++s1, ++s2, --n ) ; if (n == 0) return 0; if (*s1 == 0) return -1; if (*s2 == 0) return 1; return (*s1 - *s2); } char *strcpy(char *dest, const char *src) { char *p = dest; while (*src != 0) { *p++ = *src++; } return dest; } char *strncpy(char *dest, const char *src, size_t n) { char *p = dest; for (;*src != 0 && n > 0; --n) { *p++ = *src++; } return dest; } void *memchr(const void *s, int c, size_t n) { const unsigned char *p = s; c &= 0xFF; for (p = s; n > 0; --n, ++p) { if (*p == c) return (void*)p; } return NULL; } void *memrchr(const void *s, int c, size_t n) { const unsigned char *p = s; c &= 0xFF; for (p = s+n; n > 0; --n, --p) { if (*p == c) return (void*)p; } return NULL; } char *strchr(const char *s, int c) { c &= 0xFF; for (; *s != 0; ++s) { if (((*s) & 0xFF) == c) return (char*)s; } return NULL; } #endif aprx-2.08.svn593/LICENSE0000644000175000017500000000275712305736167013507 0ustar colincolinCopyright (c) 2007-2014, Matti Aarnio All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Matti Aarnio nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. aprx-2.08.svn593/timercmp.c0000644000175000017500000001151012306656055014447 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" /* Bits used only in the main program.. */ #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_TIME_H # include #endif #include struct timeval now; // public wall clock that can jump around struct timeval tick; // monotonic clock /* * Calculate difference from now time to target time in milliseconds. */ int tv_timerdelta_millis(struct timeval *_now, struct timeval *_target) { int deltasec = _target->tv_sec - _now->tv_sec; int deltausec = _target->tv_usec - _now->tv_usec; while (deltausec < 0) { deltausec += 1000000; --deltasec; } return deltasec * 1000 + deltausec / 1000; } /* * Add milliseconds to input parameter a returning * the result though parameter ret. */ void tv_timeradd_millis(struct timeval *ret, struct timeval *a, int millis) { if (ret != a) { // Copy if different pointers.. *ret = *a; } int usec = (int)(ret->tv_usec) + millis * 1000; if (usec >= 1000000) { int dsec = (usec / 1000000); ret->tv_sec += dsec; usec %= 1000000; // if (debug>3) printf("tv_timeadd_millis() dsec=%d dusec=%d\n",dsec, usec); } ret->tv_usec = usec; } /* * Add seconds to input parameter a returning * the result though parameter ret. */ void tv_timeradd_seconds(struct timeval *ret, struct timeval *a, int seconds) { if (ret != a) { // Copy if different pointers.. *ret = *a; } ret->tv_sec += seconds; } /* * Comparison returning -1/0/+1 depending on ( a <=> b ) * * This handles overflow wraparound of Y2038 issue of 32-bit UNIX time_t. */ int timecmp(const time_t a, const time_t b) { const int i = (int)(a - b); if (i == 0) return 0; if (i > 0) return 1; return -1; } /* * Time compare function returning -1/0/+1 depending * which parameter presents time before the other. * Zero means equals. */ int tv_timercmp(struct timeval * const a, struct timeval * const b) { // if (debug>3) { // int dt_sec = a->tv_sec - b->tv_sec; // int dt_usec = a->tv_usec - b->tv_usec; // printf("tv_timercmp(%d.%06d <=> %d.%06d) dt=%d:%06d ret= ", // a->tv_sec, a->tv_usec, b->tv_sec, b->tv_usec, dt_sec, dt_usec); // } // Time delta calculation to avoid year 2038 issue const int dt = timecmp(a->tv_sec, b->tv_sec); if (dt != 0) { // if (debug>3) printf("%ds\n", dt); return dt; } // tv_usec is always in range 0 .. 999 999 if (a->tv_usec < b->tv_usec) { // if (debug>3) printf("-1u\n"); return -1; } if (a->tv_usec > b->tv_usec) { // if (debug>3) printf("1u\n"); return 1; } // if (debug>3) printf("0\n"); return 0; // equals! } /* * Compare *tv with current time value (now), and if the difference * is more than margin seconds, then call resetfunc with resetarg. * * Usually resetarg == tv, but not always. * See */ void tv_timerbounds(const char *timername, struct timeval *tv, const int margin, void (*resetfunc)(void*), void *resetarg) { // Check that system time has not jumped too far ahead/back; // that it is within margin seconds to tv. struct timeval nowminus; struct timeval nowplus; tv_timeradd_seconds(&nowminus, &tick, -margin); // If current time MINUS margin is AFTER tv, then reset. if (tv_timercmp(tv, &nowminus) < 0) { if (debug) printf("System time has gone too much forwards, Resetting timer '%s'. dt=%d margin=%d\n", timername, (int)(tv->tv_sec - nowminus.tv_sec), margin); resetfunc(resetarg); } tv_timeradd_seconds(&nowplus, &tick, margin); // If current time PLUS margin is BEFORE tv, then reset. if (tv_timercmp(&nowplus, tv) < 0) { if (debug) printf("System time has gone too much backwards, Resetting timer '%s'. dt=%d margin=%d\n", timername, (int)(nowplus.tv_sec - tv->tv_sec), margin); resetfunc(resetarg); } } aprx-2.08.svn593/interface.c0000644000175000017500000016166712313325035014577 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" #include /* * The interface subsystem describes all interfaces in one * coherent way, independent of their actual implementation. * */ /* serial-device /dev/ttyUSB1 19200 8n1 KISS tx-ok false # receive only (default) callsign OH2XYZ-R2 # KISS subif 0 initstring "...." # initstring option timeout 900 # 900 seconds of no Rx serial-device /dev/ttyUSB2 19200 8n1 KISS initstring "...." timeout 900 # 900 seconds of no Rx callsign OH2XYZ-2 tx-ok true # This is our transmitter callsign OH2XYZ-R3 # This is receiver tx-ok false # receive only (default) tcp-device 172.168.1.1 4001 KISS tx-ok false # receive only (default) callsign OH2XYZ-R4 # KISS subif 0 initstring "...." # initstring option timeout 900 # 900 seconds of no Rx ax25-device OH2XYZ-6 # Works only on Linux systems tx-ok true # This is also transmitter */ struct aprx_interface **all_interfaces; int all_interfaces_count; int top_interfaces_group; // Init-code stores this with ifindex = 0. // This is necessary even for system where igate is removed struct aprx_interface aprsis_interface = { IFTYPE_APRSIS, 0, 0, 0, "APRSIS", {'A'<<1,'P'<<1,'R'<<1,'S'<<1,'I'<<1,'S'<<1, 0x60}, 0, NULL, 0, 0, 0, // subif, txrefcount, tx_ok 1, 1, 0, // telemeter-to-is, telemeter-to-rf, telemeter-newformat 0, NULL, NULL, #ifdef ENABLE_AGWPE NULL, #endif NULL, 0, NULL }; int interface_is_beaconable(const struct aprx_interface *aif) { switch (aif->iftype) { case IFTYPE_AX25: case IFTYPE_SERIAL: case IFTYPE_TCPIP: case IFTYPE_NULL: case IFTYPE_APRSIS: // case IFTYPE_AGWPE: // These are beaconable. return 1; default: break; } return 0; } int interface_is_telemetrable(const struct aprx_interface *aif) { // Check if the interface type is really an RF rx and/or tx switch (aif->iftype) { case IFTYPE_AX25: case IFTYPE_SERIAL: case IFTYPE_TCPIP: // case IFTYPE_AGWPE: // These are real interfaces, and telemetry sources return 1; default: break; } return 0; } #ifndef DISABLE_IGATE /* * A helper for interface_receive_ax25() - analyze 3rd-party packets received * via radio. If data content inside has path saying "TCPIP" or "TCPXX", consider * the packet to be indication that fromcall is an IGate. */ static void rx_analyze_3rdparty( historydb_t *historydb, struct pbuf_t *pb ) { const char *e = pb->data + pb->packet_len - 6; const char *p = pb->info_start; int from_igate = 0; history_cell_t *hist_rx; if (!p) return; // Bad packet.. ++p; for ( ; p < e; ++p ) { if (*p == ':') break; if (*p == ',') { // The "TCPIP*" or "TCPXX*" will always have preceding "," if (memcmp(",TCPIP*", p, 7) == 0) { from_igate = 1; break; } if (memcmp(",TCPXX*", p, 7) == 0) { from_igate = 1; break; } } // Start with 'T'. } if (!from_igate) return; // Not recognized as being sent from another TX-IGATE // OK, this packet originated from an TX-IGATE // Insert it afresh hist_rx = historydb_insert_heard(historydb, pb); if (hist_rx != NULL) { // Explicitly mark it as "received from APRSIS" // The packet was received from a TX-IGATE, therefore // the source of that packet is now logged as "from APRSIS". hist_rx->last_heard[0] = pb->t; } } #endif static char *interface_default_aliases[] = { "RELAY","WIDE","TRACE" }; static void interface_store(struct aprx_interface *aif) { if (debug) printf("interface_store() aif->callsign = '%s'\n", aif->callsign); // Init the interface specific Erlang accounting erlang_add(aif->callsign, ERLANG_RX, 0, 0); all_interfaces_count += 1; all_interfaces = realloc(all_interfaces, sizeof(*all_interfaces) * all_interfaces_count); all_interfaces[all_interfaces_count -1] = aif; if (aif->ifindex < 0) aif->ifindex = all_interfaces_count -1; if (aif->ifgroup < 0) { aif->ifgroup = all_interfaces_count; // starting at 1. the 0 is for APRSIS /* -- no hard upper limit anymore if (aif->ifgroup >= MAX_IF_GROUP) aif->ifgroup = MAX_IF_GROUP -1; */ } if (top_interfaces_group <= aif->ifgroup) top_interfaces_group = aif->ifgroup +1; } struct aprx_interface *find_interface_by_callsign(const char *callsign) { int i; for (i = 0; i < all_interfaces_count; ++i) { if ((all_interfaces[i]->callsign != NULL) && (strcasecmp(callsign, all_interfaces[i]->callsign) == 0)) { return all_interfaces[i]; } } return NULL; // Not found! } struct aprx_interface *find_interface_by_index(const int index) { if (index >= all_interfaces_count || index < 0) { return NULL; // Invalid index value } else { return all_interfaces[index]; } } static int config_kiss_subif(struct configfile *cf, struct aprx_interface *aifp, char *param1, char *str, int maxsubif) { struct aprx_interface *aif; int fail = 0; char *name; int parlen = 0; char *initstring = NULL; int initlength = 0; char *callsign = NULL; int subif = 0; int tx_ok = 0; int telemeter_to_is = 1; int telemeter_to_rf = 1; int aliascount = 0; char **aliases = NULL; int ifgroup = -1; const char *p = param1; int c; if (aifp == NULL || aifp->tty == NULL) { printf("%s:%d ERROR: on bad type of entry.\n", cf->name, cf->linenum); return 1; } for ( ; *p; ++p ) { c = *p; if ('0' <= c && c <= '9') { subif = subif * 10 + (c - '0'); } else if (c == '>') { // all fine.. break; } else { // FIXME: parameter value is bad! printf("%s:%d ERROR: name, cf->linenum, param1); return 1; } } if (subif >= maxsubif) { // FIXME: parameter value is bad! printf("%s:%d ERROR: name, cf->linenum, param1); return 1; } while (readconfigline(cf) != NULL) { if (configline_is_comment(cf)) continue; /* Comment line, or empty line */ // It can be severely indented... str = config_SKIPSPACE(cf->buf); name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRLOWER(name); param1 = str; str = config_SKIPTEXT(str, &parlen); str = config_SKIPSPACE(str); if (strcmp(name, "") == 0) { break; // End of this sub-group } if (strcmp(name, "callsign") == 0) { if (strcasecmp(param1,"$mycall") == 0) callsign = strdup(mycall); else callsign = strdup(param1); if (!validate_callsign_input(callsign,tx_ok)) { if (tx_ok) printf("%s:%d ERROR: The CALLSIGN parameter on AX25-DEVICE must be of valid AX.25 format! '%s'\n", cf->name, cf->linenum, callsign); else printf("%s:%d ERROR: The CALLSIGN parameter on AX25-DEVICE must be of valid APRSIS format! '%s'\n", cf->name, cf->linenum, callsign); fail = 1; break; } if (find_interface_by_callsign(callsign) != NULL) { // An interface with THIS callsign does exist already! printf("%s:%d ERROR: Same callsign (%s) exists already on another interface.\n", cf->name, cf->linenum, callsign); fail = 1; continue; } } else if (strcmp(name, "initstring") == 0) { if (initstring == NULL) { initlength = parlen; initstring = malloc(parlen); memcpy(initstring, param1, parlen); } else { printf("%s:%d ERROR: Double-definition of initstring parameter.\n", cf->name, cf->linenum); fail = 1; break; } } else if (strcmp(name, "tx-ok") == 0) { if (!config_parse_boolean(param1, &tx_ok)) { printf("%s:%d ERROR: Bad TX-OK parameter value -- not a recognized boolean: %s\n", cf->name, cf->linenum, param1); fail = 1; break; } } else if (strcmp(name, "telem-to-is") == 0) { if (!config_parse_boolean(param1, &telemeter_to_is)) { printf("%s:%d ERROR: Bad TELEM-TO-IS parameter value -- not a recognized boolean: %s\n", cf->name, cf->linenum, param1); fail = 1; break; } } else if (strcmp(name, "telem-to-rf") == 0) { if (!config_parse_boolean(param1, &telemeter_to_rf)) { printf("%s:%d ERROR: Bad TELEM-TO-RF parameter value -- not a recognized boolean: %s\n", cf->name, cf->linenum, param1); fail = 1; break; } } else if (strcmp(name, "alias") == 0) { char *k = strtok(param1, ","); for (; k ; k = strtok(NULL,",")) { ++aliascount; if (debug) printf(" n=%d alias='%s'\n",aliascount,k); aliases = realloc(aliases, sizeof(char*) * aliascount); aliases[aliascount-1] = strdup(k); } #ifndef DISABLE_IGATE } else if (strcmp(name, "igate-group") == 0) { // param1 = integer 1 to N. ifgroup = atol(param1); if (ifgroup < 1) { printf("%s:%d ERROR: interface 'igate-group' parameter value: '%s' is an integer with minimum value of 1.\n", cf->name, cf->linenum, param1); fail = 1; break; /* -- no hard upper limit anymore } else if (ifgroup >= MAX_IF_GROUP) { printf("%s:%d ERROR: interface 'igate-group' parameter value: '%s' is an integer with maximum value of %d.\n", cf->name, cf->linenum, param1, MAX_IF_GROUP-1); fail = 1; break; */ } #endif } else { printf("%s:%d ERROR: Unrecognized block keyword: %s\n", cf->name, cf->linenum, name); fail = 1; break; } } if (fail) { ERRORMEMFREE: if (aliases != NULL) free(aliases); if (initstring != NULL) free(initstring); return 1; // this leaks memory (but also diagnoses bad input) } if (callsign == NULL) { // FIXME: Must define at least a callsign! printf("%s:%d ERROR: MUST define CALLSIGN parameter!\n", cf->name, cf->linenum); goto ERRORMEMFREE; } if (find_interface_by_callsign(callsign) != NULL) { // An interface with THIS callsign does exist already! printf("%s:%d ERROR: Same callsign (%s) exists already on another interface.\n", cf->name, cf->linenum, callsign); goto ERRORMEMFREE; } if (debug) printf(" Defining callsign=%s txok=%s\n", subif, callsign, tx_ok ? "true":"false"); aif = malloc(sizeof(*aif)); memcpy(aif, aifp, sizeof(*aif)); aif->callsign = callsign; parse_ax25addr(aif->ax25call, callsign, 0x60); aif->subif = subif; aif->tx_ok = tx_ok; aif->telemeter_to_is = telemeter_to_is; aif->telemeter_to_rf = telemeter_to_rf; // aif->telemeter_newformat = ... aif->ifindex = -1; // system sets automatically at store time aif->ifgroup = ifgroup; // either user sets, or system sets at store time aifp->tty->interface [subif] = aif; aifp->tty->ttycallsign[subif] = callsign; #ifdef PF_AX25 /* PF_AX25 exists -- highly likely a Linux system ! */ aifp->tty->netax25 [subif] = netax25_open(callsign); #endif if (initstring != NULL) { aifp->tty->initlen[subif] = initlength; aifp->tty->initstring[subif] = initstring; } if (aliascount == 0 || aliases == NULL) { aif->aliascount = 3; aif->aliases = interface_default_aliases; } else { aif->aliascount = aliascount; aif->aliases = aliases; } return 0; } void interface_init() { interface_store( &aprsis_interface ); } int interface_config(struct configfile *cf) { struct aprx_interface *aif = calloc(1, sizeof(*aif)); char *name, *param1; char *str = cf->buf; int parlen = 0; int have_fault = 0; int maxsubif = 16; // 16 for most KISS modes, 8 for SMACK int defined_subinterface_count = 0; int ifgroup = -1; aif->iftype = IFTYPE_UNSET; aif->aliascount = 3; aif->aliases = interface_default_aliases; aif->ifindex = -1; // system sets automatically at store time aif->ifgroup = ifgroup; // either user sets, or system sets at store time aif->tx_ok = 0; aif->telemeter_to_is = 1; aif->telemeter_to_rf = 1; aif->telemeter_newformat = 0; while (readconfigline(cf) != NULL) { if (configline_is_comment(cf)) continue; /* Comment line, or empty line */ // It can be severely indented... str = config_SKIPSPACE(cf->buf); name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRLOWER(name); param1 = str; str = config_SKIPTEXT(str, &parlen); str = config_SKIPSPACE(str); if (strcmp(name, "") == 0) { // End of this interface definition // make the interface... break; } if (strcmp(name, "iftype == IFTYPE_UNSET) { aif->iftype = IFTYPE_AX25; // aif->nax25p = NULL; } else { printf("%s:%d ERROR: Only single device specification per interface block!\n", cf->name, cf->linenum); have_fault = 1; continue; } if (strcasecmp(param1,"$mycall") == 0) param1 = strdup(mycall); if (!validate_callsign_input(param1,1)) { printf("%s:%d ERROR: The CALLSIGN parameter on AX25-DEVICE must be of valid AX.25 format! '%s'\n", cf->name, cf->linenum, param1); have_fault = 1; continue; } if (find_interface_by_callsign(param1) != NULL) { // An interface with THIS callsign does exist already! printf("%s:%d ERROR: Same callsign (%s) exists already on another interface.\n", cf->name, cf->linenum, param1); have_fault = 1; continue; } if (debug) printf("%s:%d: AX25-DEVICE '%s' '%s'\n", cf->name, cf->linenum, param1, str); aif->callsign = strdup(param1); parse_ax25addr(aif->ax25call, aif->callsign, 0x60); aif->nax25p = netax25_addrxport(param1, aif); if (aif->nax25p == NULL) { printf("%s:%d ERROR: Failed to open this AX25-DEVICE: '%s'\n", cf->name, cf->linenum, param1); have_fault = 1; continue; } #else printf("%s:%d ERROR: AX25-DEVICE interfaces are not supported at this system!\n", cf->name, cf->linenum, param1); have_fault = 1; #endif } else if ((strcmp(name,"serial-device") == 0) && (aif->tty == NULL)) { if (aif->iftype == IFTYPE_UNSET) { aif->iftype = IFTYPE_SERIAL; aif->tty = ttyreader_new(); aif->tty->ttyname = strdup(param1); aif->tty->interface[0] = aif; aif->tty->ttycallsign[0] = mycall; // end processing registers it } else { printf("%s:%d ERROR: Only single device specification per interface block!\n", cf->name, cf->linenum); have_fault = 1; continue; } if (debug) printf(".. new style serial: '%s' '%s'.. tncid=0\n", aif->tty->ttyname, str); have_fault |= ttyreader_parse_ttyparams(cf, aif->tty, str); switch (aif->tty->linetype) { case LINETYPE_KISSSMACK: maxsubif = 8; // 16 for most KISS modes, 8 for SMACK break; case LINETYPE_KISSFLEXNET: // ??? break; default: break; } // Always count as defined, even when an error happened! ++defined_subinterface_count; } else if ((strcmp(name,"tcp-device") == 0) && (aif->tty == NULL)) { int len; char *host, *port; if (aif->iftype == IFTYPE_UNSET) { aif->iftype = IFTYPE_TCPIP; aif->tty = ttyreader_new(); aif->tty->interface[0] = aif; aif->tty->ttycallsign[0] = mycall; // end-step processing registers it } else { printf("%s:%d ERROR: Only single device specification per interface block!\n", cf->name, cf->linenum); have_fault = 1; continue; } host = param1; port = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (debug) printf(".. new style tcp!: '%s' '%s' '%s'..\n", host, port, str); len = strlen(host) + strlen(port) + 8; aif->tty->ttyname = malloc(len); sprintf((char *) (aif->tty->ttyname), "tcp!%s!%s!", host, port); have_fault |= ttyreader_parse_ttyparams( cf, aif->tty, str ); switch (aif->tty->linetype) { case LINETYPE_KISSSMACK: maxsubif = 8; // 16 for most KISS modes, 8 for SMACK break; case LINETYPE_KISSFLEXNET: // ??? break; default: break; } // Always count as defined, even when an error happened! ++defined_subinterface_count; } else if (strcmp(name,"null-device") == 0) { if (aif->iftype == IFTYPE_UNSET) { aif->iftype = IFTYPE_NULL; // aif->nax25p = NULL; } else { printf("%s:%d ERROR: Only single device specification per interface block!\n", cf->name, cf->linenum); have_fault = 1; continue; } aif->tx_ok = 1; if (strcasecmp(param1,"$mycall") == 0) param1 = strdup(mycall); if (find_interface_by_callsign(param1) != NULL) { // An interface with THIS callsign does exist already! printf("%s:%d ERROR: Same callsign (%s) exists already on another interface.\n", cf->name, cf->linenum, param1); have_fault = 1; continue; } if (!have_fault) { aif->iftype = IFTYPE_TCPIP; aif->tty = ttyreader_new(); aif->tty->interface[0] = aif; aif->tty->ttycallsign[0] = mycall; } have_fault |= ttyreader_parse_nullparams(cf, aif->tty, str); if (debug) printf("%s:%d: NULL-DEVICE '%s' '%s'\n", cf->name, cf->linenum, param1, str); aif->callsign = strdup(param1); parse_ax25addr(aif->ax25call, aif->callsign, 0x60); #ifdef ENABLE_AGWPE } else if ((strcmp(name,"agwpe-device") == 0) && (aif->tty == NULL)) { // agwpe-device hostname hostport callsign agwpeportnum int len; const char *hostname, *hostport; char *callsign, *agwpeportnum; if (aif->iftype == IFTYPE_UNSET) { aif->iftype = IFTYPE_AGWPE; aif->tty = ttyreader_new(); aif->tty->interface[0] = aif; aif->tty->ttycallsign[0] = mycall; // end-step processing registers it } else { printf("%s:%d ERROR: Only single device specification per interface block!\n", cf->name, cf->linenum); have_fault = 1; continue; } hostname = strdup(param1); hostport = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); hostport = strdup(hostport); callsign = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); agwpeportnum = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (debug) printf(".. AGWPE-DEVICE: '%s' '%s' '%s' '%s' ('%s'...)\n", hostname, hostport, callsign, agwpeportnum, str); len = strlen(hostname) + strlen(hostport) + strlen(agwpeportnum) + 8; aif->tty->ttyname = malloc(len); sprintf((char *) (aif->tty->ttyname), "tcp!%s!%s[%s]", hostname, hostport, agwpeportnum); if (strcasecmp(callsign,"$mycall") == 0) callsign = strdup(mycall); else callsign = strdup(callsign); if (!validate_callsign_input(callsign,1)) { printf("%s:%d ERROR: The CALLSIGN parameter on AGWPE-DEVICE must be of valid AX.25 format! '%s'\n", cf->name, cf->linenum, callsign); have_fault = 1; continue; } if (find_interface_by_callsign(callsign) != NULL) { // An interface with THIS callsign does exist already! printf("%s:%d ERROR: Same callsign (%s) exists already on another interface.\n", cf->name, cf->linenum, callsign); have_fault = 1; continue; } aif->callsign = callsign; parse_ax25addr(aif->ax25call, aif->callsign, 0x60); aif->agwpe = agwpe_addport(hostname, hostport, agwpeportnum, aif); if (aif->agwpe == NULL) { printf("%s:%d ERROR: Failed to setup this AGWPE-DEVICE: '%s'\n", cf->name, cf->linenum, callsign); have_fault = 1; continue; } // Always count as defined, even when an error happened! ++defined_subinterface_count; #endif } else if (strcmp(name,"tx-ok") == 0) { int bool; if (!config_parse_boolean(param1, &bool)) { printf("%s:%d ERROR: Bad TX-OK parameter value -- not a recognized boolean: %s\n", cf->name, cf->linenum, param1); have_fault = 1; continue; } aif->tx_ok = bool; if (bool && aif->callsign) { if (!validate_callsign_input(aif->callsign,bool)) { // Transmitters REQUIRE valid AX.25 address printf("%s:%d: ERROR: TX-OK 'TRUE' -- BUT PREVIOUSLY SET CALLSIGN IS NOT VALID AX.25 ADDRESS \n", cf->name, cf->linenum); continue; } } } else if (strcmp(name, "telem-to-is") == 0) { int bool; if (!config_parse_boolean(param1, &bool)) { printf("%s:%d ERROR: Bad TELEM-TO-IS parameter value -- not a recognized boolean: %s\n", cf->name, cf->linenum, param1); have_fault = 1; break; } aif->telemeter_to_is = bool; } else if (strcmp(name, "telem-to-rf") == 0) { int bool; if (!config_parse_boolean(param1, &bool)) { printf("%s:%d ERROR: Bad TELEM-TO-RF parameter value -- not a recognized boolean: %s\n", cf->name, cf->linenum, param1); have_fault = 1; break; } aif->telemeter_to_rf = bool; } else if (strcmp(name,"timeout") == 0) { if (config_parse_interval(param1, &(aif->timeout) ) || (aif->timeout < 0) || (aif->timeout > 1200)) { aif->timeout = 0; printf("%s:%d ERROR: Bad TIMEOUT parameter value: '%s' accepted range: 0 to 1200 seconds.\n", cf->name, cf->linenum, param1); have_fault = 1; continue; } if (aif->tty != NULL) { aif->tty->read_timeout = aif->timeout; } } else if (strcmp(name, "callsign") == 0) { if (strcasecmp(param1,"$mycall") == 0) param1 = strdup(mycall); if (find_interface_by_callsign(param1) != NULL) { // An interface with THIS callsign does exist already! printf("%s:%d ERROR: Same callsign (%s) exists already on another interface.\n", cf->name, cf->linenum, param1); have_fault = 1; continue; } if (!validate_callsign_input(param1, aif->tx_ok)) { if (aif->tx_ok && aif->iftype != IFTYPE_NULL) { printf("%s:%d ERROR: The CALLSIGN parameter on transmit capable interface must be of valid AX.25 format! '%s'\n", cf->name, cf->linenum, param1); have_fault = 1; continue; } } if (aif->callsign != NULL) free(aif->callsign); aif->callsign = strdup(param1); parse_ax25addr(aif->ax25call, aif->callsign, 0x60); if (aif->tty != NULL) aif->tty->ttycallsign[0] = aif->callsign; if (debug) printf(" callsign= '%s'\n", aif->callsign); } else if (strcmp(name, "initstring") == 0) { if (aif->tty != NULL) { int initlength = parlen; char *initstring = malloc(parlen); memcpy(initstring, param1, parlen); aif->tty->initstring[0] = initstring; aif->tty->initlen[0] = initlength; } } else if (strcmp(name, "alias") == 0) { char *k = strtok(param1, ","); if (aif->aliases == interface_default_aliases) { aif->aliascount = 0; aif->aliases = NULL; } for (; k ; k = strtok(NULL,",")) { aif->aliascount += 1; if (debug) printf(" n=%d alias='%s'\n",aif->aliascount,k); aif->aliases = realloc(aif->aliases, sizeof(char*) * aif->aliascount); aif->aliases[aif->aliascount-1] = strdup(k); } #ifndef DISABLE_IGATE } else if (strcmp(name, "igate-group") == 0) { // param1 = integer 1 to N. ifgroup = atol(param1); if (ifgroup < 1) { printf("%s:%d ERROR: interface 'igate-group' parameter value: '%s' is an integer with minimum value of 1.\n", cf->name, cf->linenum, param1); have_fault = 1; continue; /* -- no hard upper limit anymore } else if (ifgroup >= MAX_IF_GROUP) { printf("%s:%d ERROR: interface 'igate-group' parameter value: '%s' is an integer with maximum value of %d.\n", cf->name, cf->linenum, param1, MAX_IF_GROUP-1); have_fault = 1; continue; */ } #endif } else { printf("%s:%d ERROR: Unknown config entry name: '%s'\n", cf->name, cf->linenum, name); have_fault = 1; } } while (!have_fault && aif->callsign == NULL && (aif->iftype == IFTYPE_SERIAL || aif->iftype == IFTYPE_TCPIP) && defined_subinterface_count == 1) { // First check if there already is an interface with $mycall // callsign on it.. if (find_interface_by_callsign(mycall) != NULL) { // An interface with $MYCALL callsign does exist already! printf("%s:%d ERROR: The $MYCALL callsign (%s) exists already on another interface.\n", cf->name, cf->linenum, mycall); have_fault = 1; break; } // Supply a default value aif->callsign = strdup(mycall); parse_ax25addr(aif->ax25call, aif->callsign, 0x60); #ifdef PF_AX25 // PF_AX25 exists -- highly likely a Linux system ! // With enough defaults being used, the callsign is defined // by global "macro" mycall, and never ends up activating // the tty -> linux kernel kiss/smack pty interface. // This part does that final step for minimalistic config. if (aif->tty != NULL && aif->tty->netax25[0] == NULL && aif->tty->ttycallsign[0] != NULL) { aif->tty->netax25[0] = netax25_open(aif->tty->ttycallsign[0]); } #endif // Done it, leave.. break; } if (!have_fault) { int i; if (aif->tty != NULL) { // Register all tty subinterfaces if (debug) printf(" .. store tty subinterfaces\n"); for (i = 0; i < maxsubif; ++i) { if (aif->tty->interface[i] != NULL) { if (debug) printf(" .. store interface[%d] callsign='%s'\n",i, aif->tty->interface[i]->callsign); interface_store(aif->tty->interface[i]); } } } else { // Not TTY multiplexed ( = KISS ) interface, // register just the primary. aif->ifgroup = ifgroup; // either user sets, or system sets at store time interface_store(aif); if (debug) printf(" .. store other interface\n"); } if (aif->iftype == IFTYPE_SERIAL) ttyreader_register(aif->tty); if (aif->iftype == IFTYPE_TCPIP) ttyreader_register(aif->tty); } else { if (aif->callsign) free(aif->callsign); if (aif->tty) { if (aif->tty->ttyname) free((void*)(aif->tty->ttyname)); } free(aif); } // coverity[leaked_storage] return have_fault; } /* * Process received AX.25 packet * - from AIF do find all DIGIPEATERS wanting this source. * - If there are none, end processing. * - Parse the received frame for possible latter filters * - Feed the resulting parsed packet to each digipeater * * * Tx-IGate rules: * // 2) - sending station has not been heard recently // on radio // 1) - verify receiving station has been heard // recently on radio // 4) - the receiving station has not been heard via // the Internet within a predefined time period. // (Note that _this_ packet is heard from internet, // so one must not confuse this to history.. // Nor this siblings that are being created // one for each tx-interface...) // // A station is said to be heard via the Internet if packets // from the station contain TCPIP* or TCPXX* in the header or // if gated (3rd-party) packets are seen on RF gated by the // station and containing TCPIP or TCPXX in the 3rd-party // header (in other words, the station is seen on RF as being // an IGate). * * That is, this part of code collects knowledge of RF-wise near-by TX-IGATEs. */ void interface_receive_ax25(const struct aprx_interface *aif, const char *ifaddress, const int is_aprs, const int ui_pid, const uint8_t *axbuf, const int axaddrlen, const int axlen, const char *tnc2buf, const int tnc2addrlen, const int tnc2len) { int i; int digi_like_aprs = is_aprs; if (aif == NULL) return; // Not a real interface for digi use if (aif->digisourcecount == 0) { if (debug>1) printf("interface_receive_ax25() no receivers for source %s\n",aif->callsign); return; // No receivers for this source } if (debug) printf("interface_receive_ax25() from %s axlen=%d tnc2len=%d\n",aif->callsign,axlen,tnc2len); if (axaddrlen <= 14) return; // SOURCE>DEST without any VIAs.. // Note: Above one disables MICe destaddress-SSID embedded // extremely compressed WIDEn-N notation. // FIXME: match ui_pid to list of UI PIDs that are treated with similar // digipeat rules as is APRS New-N. // ui_pid < 0 means that this frame is not an UI frame at all. if (ui_pid >= 0) digi_like_aprs = 1; // FIXME: more precise matching? for (i = 0; i < aif->digisourcecount; ++i) { struct digipeater_source *digisource = aif->digisources[i]; #ifndef DISABLE_IGATE // Transmitter's HistoryDB historydb_t *historydb = digisource->parent->historydb; #endif // Allocate pbuf, it is born "gotten" (refcount == 1) struct pbuf_t *pb = pbuf_new(is_aprs, digi_like_aprs, tnc2addrlen, tnc2buf, tnc2len, axaddrlen, axbuf, axlen); if (pb == NULL) { // Urgh! Can't do a thing to this! // Likely reason: axlen+tnc2len > 2100 bytes! continue; } pb->source_if_group = aif->ifgroup; // If APRS packet, then parse for APRS meaning ... if (is_aprs) { int rc = parse_aprs(pb, #ifndef DISABLE_IGATE historydb #else NULL #endif ); // don't look inside 3rd party char *srcif = aif->callsign; if (debug) printf(".. parse_aprs() rc=%s type=0x%02x srcif=%s tnc2addr='%s' info_start='%s'\n", rc ? "OK":"FAIL", pb->packettype, srcif, pb->data, pb->info_start); // If there are no filters, permit all packets if (digisource->src_filters != NULL) { int filter_discard = filter_process(pb, digisource->src_filters, #ifndef DISABLE_IGATE historydb // Transmitter HistoryDB #else NULL #endif ); // filter_discard > 0: accept // filter_discard = 0: indifferent (not reject, not accept), tx-igate rules as is. // filter_discard < 0: reject if (debug) printf("source filtering result: %s\n", (filter_discard < 0 ? "DISCARD" : (filter_discard > 0 ? "ACCEPT" : "no-match"))); if (filter_discard <= 0) { pbuf_put(pb); continue; // allow only explicitly accepted } } #ifndef DISABLE_IGATE // Find out IGATE callsign (if any), and record it on transmitter's historydb. if (pb->packettype & T_THIRDPARTY) { rx_analyze_3rdparty( historydb, pb ); } else { // Everything else, feed to history-db historydb_insert_heard( historydb, pb ); } #endif } // Feed it to digipeater ... digipeater_receive( digisource, pb); // .. and finally free up the pbuf (if refcount goes to zero) pbuf_put(pb); } } /* * Process AX.25 packet transmit; beacons, digi output, igate output... * * - aif: output interface * - axaddr: ax.25 address * - axdata: payload content, with control and PID bytes prefixing them */ void interface_transmit_ax25(const struct aprx_interface *aif, uint8_t *axaddr, const int axaddrlen, const char *axdata, const int axdatalen) { int axlen = axaddrlen + axdatalen; uint8_t *axbuf; if (debug) { const char *callsign = ""; if (aif != NULL) callsign=aif->callsign; printf("interface_transmit_ax25(aif=%p[%s], .., axlen=%d)\n", aif, callsign, axlen); } if (axlen == 0) return; if (aif == NULL) return; switch (aif->iftype) { case IFTYPE_SERIAL: case IFTYPE_TCPIP: // If there is linetype error, kisswrite detects it. // Make it into single buffer to give to KISS sender if (debug>2) { printf("serial_sendto() len=%d,%d: ",axaddrlen,axdatalen); hexdumpfp(stdout, axaddr, axaddrlen, 1); printf(" // "); hexdumpfp(stdout, (uint8_t*)axdata, axdatalen, 0); printf("\n"); } axbuf = alloca(axlen); memcpy(axbuf, axaddr, axaddrlen); memcpy(axbuf + axaddrlen, axdata, axdatalen); kiss_kisswrite(aif->tty, aif->subif, axbuf, axlen); break; #ifdef PF_AX25 /* PF_AX25 exists -- highly likely a Linux system ! */ case IFTYPE_AX25: // The Linux netax25 sender takes same data as this interface netax25_sendto( aif->nax25p, axaddr, axaddrlen, axdata, axdatalen ); break; #endif #ifdef ENABLE_AGWPE case IFTYPE_AGWPE: agwpe_sendto( aif->agwpe, axaddr, axaddrlen, axdata, axdatalen ); break; #endif case IFTYPE_NULL: // Efficient transmitter :-) if (debug>1) printf("tx null-device: %s\n", aif->callsign); if (debug>2) { printf("null_sendto() len=%d,%d ",axaddrlen,axdatalen); hexdumpfp(stdout, axaddr, axaddrlen, 1); printf(" // "); hexdumpfp(stdout, (uint8_t*)axdata, axdatalen, 0); printf("\n"); } // Account the transmission anyway ;-) erlang_add(aif->callsign, ERLANG_TX, axaddrlen+axdatalen + 10, 1); break; default: break; } } #ifndef DISABLE_IGATE /* * Process received AX.25 packet -- for APRSIS * - from AIF do find all DIGIPEATERS wanting this source. * - If there are none, end processing. * - Parse the received frame for possible latter filters * - Feed the resulting parsed packet to each digipeater * * See: http://www.aprs-is.net/IGateDetails.aspx * * Paths * * IGates should use the 3rd-party format on RF of * IGATECALL>APRS,GATEPATH}FROMCALL>TOCALL,TCPIP,IGATECALL*:original packet data * where GATEPATH is the path that the gated packet is to follow * on RF. This format will allow IGates to prevent gating the packet * back to APRS-IS. * * q constructs should never appear on RF. * The I construct should never appear on RF. * Except for within gated packets, TCPIP and TCPXX should not be * used on RF. * * Part of the Tx-IGate logic is here because we use pbuf_t data blocks: * * 1) The receiving station has been heard recently * within defined range limits, and more recently * than since given interval T1. (Range as digi-hops [N1] * or coordinates, or both.) * * 2) The sending station has not been heard via RF * within timer interval T2. (Third-party relayed * frames are not analyzed for this.) * * 4) the receiving station has not been heard via the Internet * within a predefined time period. * A station is said to be heard via the Internet if packets * from the station contain TCPIP* or TCPXX* in the header or * if gated (3rd-party) packets are seen on RF gated by the * station and containing TCPIP or TCPXX in the 3rd-party * header (in other words, the station is seen on RF as being * an IGate). * * 5) Gate all packets to RF based on criteria set by the sysop * (such as callsign, object name, etc.). * * c) Drop everything else. */ static uint8_t toaprs[7] = { 'A'<<1,'P'<<1,'R'<<1,'S'<<1,' '<<1,' '<<1,0x60 }; void interface_receive_3rdparty( const struct aprx_interface *aif, const char *fromcall, const char *origtocall, const char *gwtype, const char *tnc2data, const int tnc2datalen ) { int d; // digipeater index char tnc2buf1[2800]; uint8_t ax25buf1[2800]; time_t recent_time = tick.tv_sec - 3600; // "recent" = 1 hour uint16_t filter_packettype = 0; int ax25addrlen1; int ax25len1; int rc, tnc2addrlen1, tnc2len1; uint8_t *a; char *t; struct pbuf_t *pb; if (debug) printf("interface_receive_3rdparty() aif=%p, aif->digicount=%d\n", aif, aif ? aif->digisourcecount : -1); if (aif == NULL) { return; // Not a real interface for digi use } // We have to recognize incoming messages targeted to // this server. For this we need to parse the TNC2 frame. // // We have a also filter statements to process here, // we need to turn incoming APRSIS frame to something // that the filter can process: // Incoming: // EI7IG-1>APRS,TCPIP*,qAC,T2IRELAND:@262231z5209.97N/00709.65W_238/019g019t049P006h95b10290.wview_5_19_0 // Filtered: // EI7IG-1>APRS:@262231z5209.97N/00709.65W_238/019g019t049P006h95b10290.wview_5_19_0 a = ax25buf1; parse_ax25addr( a, tocall, 0x60 ); a += 7; parse_ax25addr( a, fromcall, 0x60 ); a += 7; // No need to add generated VIA address component // to this filter input data a[-1] |= 0x01; // end-of-address bit ax25addrlen1 = a - ax25buf1; *a++ = 0x03; *a++ = 0xF0; if ((sizeof(ax25buf1) - tnc2datalen) <= (a-ax25buf1)) { if (debug) printf(" .. data does not fit on ax25buf"); return; } memcpy( a, tnc2data, tnc2datalen ); a += tnc2datalen; ax25len1 = (a - ax25buf1); t = tnc2buf1; t += sprintf(t, "%s>%s:", fromcall, origtocall); tnc2addrlen1 = t - tnc2buf1 - 1; if ((sizeof(tnc2buf1) - tnc2datalen) <= (t-tnc2buf1)) { if (debug) printf(" .. data does not fit on tnc2buf"); return; } memcpy(t, tnc2data, tnc2datalen); t += tnc2datalen; tnc2len1 = (t - tnc2buf1); // Allocate temporary pbuf for filter call use pb = pbuf_new(1 /*is_aprs*/, 1 /* digi_like_aprs */, tnc2addrlen1, tnc2buf1, tnc2len1, ax25addrlen1, ax25buf1, ax25len1); if (pb == NULL) { // Urgh! Can't do a thing to this! // Likely reason: ax25len+tnc2len > 2100 bytes! if (debug) printf("pbuf_new() returned NULL! Discarding!\n"); return; } pb->source_if_group = 0; // 3rd-party frames are always from APRSIS // This is APRS packet, parse for APRS meaning ... rc = parse_aprs(pb, NULL); // look inside 3rd party -- historydb is looked up again below if (debug) { const char *srcif = aif->callsign ? aif->callsign : "??"; printf(".. parse_aprs() rc=%s type=0x%02x srcif=%s tnc2addr='%s' info_start='%s'\n", rc ? "OK":"FAIL", pb->packettype, srcif, pb->data, pb->info_start); } filter_packettype = pb->packettype; // Check if it is a message destined to myself, and process if so. rc = process_message_to_myself(aif, pb); // Drop the temporary pbuf.. pbuf_put(pb); if (rc != 0) { return; // Processed as message-to-myself } if (aif->digisourcecount == 0) { return; // No receivers for this source } // Feed it to digipeaters ... for (d = 0; d < aif->digisourcecount; ++d) { struct digipeater_source *digisrc = aif->digisources[d]; struct digipeater *digi = digisrc->parent; struct aprx_interface *tx_aif = digi->transmitter; #ifndef DISABLE_IGATE historydb_t *historydb = digi->historydb; #endif char *srcif; int discard_this, filter_discard; char tnc2buf[2800]; uint8_t ax25buf[2800]; int ax25addrlen, ax25len; int tnc2addrlen, tnc2len; // This is APRS packet, parse for APRS meaning ... rc = parse_aprs(pb, #ifndef DISABLE_IGATE historydb // Transmitter HistoryDB #else NULL #endif ); // look inside 3rd party -- TODO: but what HISTORYDB ? if (debug) { const char *srcif = aif->callsign ? aif->callsign : "??"; printf(".. parse_aprs() rc=%s type=0x%02x srcif=%s tnc2addr='%s' info_start='%s'\n", rc ? "OK":"FAIL", pb->packettype, srcif, pb->data, pb->info_start); } // Produced 3rd-party packet: // IGATECALL>APRS,GATEPATH:}FROMCALL>TOCALL,TCPIP,IGATECALL*:original packet data if (debug) printf("## produce 3rd-party frame for transmit:\n"); // Parse the TNC2 format to AX.25 format // using ax25buf[] storage area. memcpy(ax25buf, toaprs, 7); // AX.25 DEST call // FIXME: should this be IGATECALL, not tx_aif->ax25call ?? memcpy(ax25buf+7, tx_aif->ax25call, 7); // AX.25 SRC call a = ax25buf + 2*7; if ((filter_packettype & T_MESSAGE) != 0 && digisrc->msg_path != NULL) { if (digisrc->msg_path != NULL) { memcpy(a, digisrc->msgviapath, 7); // AX.25 VIA call for a Message a += 7; } } else { if (digisrc->via_path != NULL) { memcpy(a, digisrc->ax25viapath, 7); // AX.25 VIA call a += 7; } } *(a-1) |= 0x01; // DEST,SRC(,VIA1) - end-of-address bit ax25addrlen = a - ax25buf; if (debug>2) { printf("ax25hdr "); hexdumpfp(stdout, ax25buf, ax25addrlen, 1); printf("\n"); } *a++ = 0x03; // UI *a++ = 0xF0; // PID = 0xF0 a += sprintf((char*)a, "}%s>%s,%s,%s*:", fromcall, origtocall, gwtype, tx_aif->callsign ); ax25len = a - ax25buf; if (tnc2datalen + ax25len > sizeof(ax25buf)) { // Urgh... Can not fit it in :-( if(debug)printf("data does not fit into ax25buf: %d > %d\n", tnc2datalen+ax25len, (int)sizeof(ax25buf)); continue; } memcpy(a, tnc2data, tnc2datalen); ax25len += tnc2datalen; // AX.25 packet is built, now build TNC2 version of it t = tnc2buf; t += sprintf(t, "%s>%s", tx_aif->callsign, tocall); if ((filter_packettype & T_MESSAGE) != 0 && digisrc->msg_path != NULL) { if (digisrc->msg_path != NULL) { t += sprintf(t, ",%s", digisrc->msg_path); } } else { if (digisrc->via_path != NULL) { t += sprintf(t, ",%s", digisrc->via_path); } } if (debug>1)printf(" tnc2addr = %s\n", tnc2buf); tnc2addrlen = t - tnc2buf; *t++ = ':'; t += sprintf(t, "}%s>%s,%s,%s*:", fromcall, origtocall, gwtype, tx_aif->callsign ); if (tnc2datalen + (t-tnc2buf) +4 > sizeof(tnc2buf)) { // Urgh... Can not fit it in :-( if(debug)printf("data does not fit into tnc2buf: %d > %d\n", (int)(tnc2datalen+(t-tnc2buf)+4), (int)sizeof(tnc2buf)); continue; } memcpy(t, tnc2data, tnc2datalen); t += tnc2datalen; tnc2len = (t - tnc2buf); // Allocate pbuf, it is born "gotten" (refcount == 1) pb = pbuf_new(1 /*is_aprs*/, 1 /* digi_like_aprs */, tnc2addrlen, tnc2buf, tnc2len, ax25addrlen, ax25buf, ax25len); if (pb == NULL) { // Urgh! Can't do a thing to this! // Likely reason: ax25len+tnc2len > 2100 bytes! if (debug) printf("pbuf_new() returned NULL! Discarding!\n"); continue; } pb->source_if_group = 0; // 3rd-party frames are always from APRSIS srcif = aif->callsign ? aif->callsign : "??"; // This is APRS packet, parse for APRS meaning ... rc = parse_aprs(pb, historydb); // look inside 3rd party if (debug) printf(".. parse_aprs() rc=%s type=0x%02x srcif=%s tnc2addr='%s' info_start='%s'\n", rc ? "OK":"FAIL", pb->packettype, srcif, pb->data, pb->info_start); // 1) - verify receiving station has been heard // recently on radio // 2) - sending station has not been heard recently // on radio // 4) - the receiving station has not been heard via // the Internet within a predefined time period. // (Note that _this_ packet is heard from internet, // so one must not confuse this to history.. // Nor this siblings that are being created // one for each tx-interface...) // // A station is said to be heard via the Internet if packets // from the station contain TCPIP* or TCPXX* in the header or // if gated (3rd-party) packets are seen on RF gated by the // station and containing TCPIP or TCPXX in the 3rd-party // header (in other words, the station is seen on RF as being // an IGate). // Message Tx-IGate rules.. discard_this = 0; if (pb->dstname == NULL) { // Sanity -- not a message.. discard_this = 1; } if (filter_packettype == 0) filter_packettype = pb->packettype; if ((filter_packettype & T_MESSAGE) == 0) { // Not a message packet discard_this = 1; } if ((filter_packettype & (T_NWS)) != 0) { // Not a weather alert packet discard_this = 1; } // Accept/Reject the packet by digipeater rx filter? filter_discard = 0; if (digisrc->src_filters == NULL) { // No filters defined, default Tx-iGate rules apply } else { if (debug) printf("## process source filter\n"); { // Stores position, and message references void *v = historydb_insert_heard( historydb, pb ); if (debug) printf("historydb_insert_heard(APRSIS) v=%p\n", v); } filter_discard = filter_process(pb, digisrc->src_filters, historydb); if (debug) printf("filter says: %d (%s)\n", filter_discard, (filter_discard > 0 ? "accept" : (filter_discard == 0 ? "indifferent" : "reject"))); // filter_discard > 0: accept // filter_discard = 0: indifferent (not reject, not accept), tx-igate rules as is. // filter_discard < 0: reject // Manual filter says: Reject! if (filter_discard < 0) { if (debug) printf("REJECTED!\n"); discard_this = 1; } // Manual filter says: Accept! if (discard_this && filter_discard > 0) { if (debug) printf("filters say: send!\n"); discard_this = 0; } } if (!discard_this && pb->dstname != NULL) { // 1) - verify receiving station has been heard // recently on radio char recipient[10]; history_cell_t *hist_rx; int i = 0; while ( i < 9 && pb->dstname[i] != 0 && pb->dstname[i] != ' ' ) { recipient[i] = pb->dstname[i]; ++i; } recipient[i] = 0; pb->dstname_len = strlen(recipient); // FIXME? Should test all SSIDs of this target callsign, // not just this one target, // if this is a T_MESSAGE! (strange BoB rules...) hist_rx = historydb_lookup(historydb, recipient, strlen(recipient)); if (hist_rx == NULL) { if (debug) printf("No history entry for receiving call: '%s' DISCARDING.\n", recipient); discard_this = 1; } // See that it has 'heard on radio' flag on this tx interface if (hist_rx != NULL && discard_this == 0) { if (timecmp(hist_rx->last_heard[tx_aif->ifgroup], recent_time) >= 0) { // Heard recently enough discard_this = 0; if (debug) printf("History entry for receiving call '%s' from RADIO is recent enough. KEEPING.\n", recipient); } } // FIXME: Check that recipient is in our service area // a) coordinate is "near by" // b) last known hop-count is low enough // (FIXME: RF hop-count recording infra needed!) // 4) the receiving station has not been heard via the internet if (hist_rx != NULL && timecmp(hist_rx->last_heard[0], recent_time) > 0) { // "is heard recently via internet" discard_this = 1; if (debug) printf("History entry for sending call '%s' from APRSIS is too new. DISCARDING.\n", fromcall); } } if (!discard_this) { history_cell_t *hist_tx = historydb_lookup(historydb, fromcall, strlen(fromcall)); // If no history entry for this tx callsign, // then rules 2 and 4 permit tx-igate if (hist_tx != NULL) { // There is a history entry for this tx callsign, check rules 2+4 // 2) Sending station has not been heard recently on radio (this target) if (timecmp(hist_tx->last_heard[tx_aif->ifgroup], recent_time) > 0) { // "is heard recently" discard_this = 1; if (debug) printf("History entry for sending call '%s' from RADIO is too new. DISCARDING.\n", fromcall); } } } { // Stores position, and message references void *v = historydb_insert_heard( historydb, pb ); if (debug) printf("historydb_insert_heard(APRSIS) v=%p\n",v); } if (filter_discard > 0 || (filter_discard == 0 && !discard_this)) { // Not discarding - approved for transmission if ((filter_packettype & T_POSITION) == 0) { // TODO: For position-less packets send at first a position packet // for same source call sign -- if available. } if (debug) printf("Send to digipeater\n"); digipeater_receive( digisrc, pb); } else { if (debug) printf("DISCARDED! (filter_discard=%d, discard_this=%d)\n",filter_discard, discard_this); } // .. and finally free up the pbuf (if refcount goes to 0) pbuf_put(pb); } } /* * See if this is a message that is destined to myself */ #define DSTNAMELEN 16 /* 8+1+2+1 = 12, use 16 for stack align */ static int dstname_is_myself(const struct pbuf_t*const pb, char *dstname, const struct aprx_interface**aifp) { struct aprx_interface *aif; // Copy message destination, if available. *dstname = 0; // always clear first.. if (pb->dstname != NULL) { strncpy(dstname, pb->dstname, DSTNAMELEN-1); dstname[DSTNAMELEN-1] = 0; } if (strcmp(dstname, mycall) == 0) { // To MYCALL account return 1; } if (aprsis_loginid != NULL && strcmp(dstname, aprsis_loginid) == 0) { // To APRSIS login account return 1; } // Maybe one of my transmitters? aif = find_interface_by_callsign(dstname); if (aif != NULL && aif->tx_ok) { // To one of my transmitter interfaces *aifp = aif; return 1; } // None of my identities return 0; } /* * Ack the message */ static void ack_message(const struct aprx_interface *const srcif, const struct aprx_interface *const aif, const struct pbuf_t*const pb, const struct aprs_message_t*const am, const char*const dstname) { // ACK message to APRSIS is simple(ish), routing it is another thing.. if (srcif == &aprsis_interface) { char destbuf[50]; int destlen = sprintf(destbuf, "%s>APRS,TCPIP*", dstname); char txt[50]; int txtlen; char *t = txt; const char *s = pb->srcname; int i; *t++ = ':'; for (i = 0; i < 9 && i < pb->srcname_len; ++i) { *t++ = *s++; } for ( ; i < 9 ; ++i) { *t++ = ' '; } *t++ = ':'; *t++ = 'a'; *t++ = 'c'; *t++ = 'k'; for (i = 0, s = am->msgid; i < am->msgid_len; ++i) { *t++ = *s++; } txtlen = t - txt; aprsis_queue(destbuf, destlen, qTYPE_LOCALGEN, aprsis_login, txt, txtlen); return; } // TODO: ACK things sent via radio interfaces? } /* * A message is destined to myself, lets look closer.. * Return non-zero if it was recognized as targeted to this node. */ int process_message_to_myself(const struct aprx_interface*const srcif, const struct pbuf_t*const pb) { struct aprs_message_t am; int rc; const struct aprx_interface*aif = srcif; char dstname[DSTNAMELEN]; if ((pb->packettype & T_MESSAGE) == 0) { return 0; // Not a message! } if (!dstname_is_myself(pb, dstname, &aif)) { // Not destined to me // This will also reject bulletins, which one is not supposed to ACK anyway.. return 0; } rc = parse_aprs_message(pb, &am); if (rc != 0) { // Not acceptable parse result return 0; } // Whatever message, syslog it. syslog(LOG_INFO, "%*s", pb->packet_len, pb->data); if (am.is_rej || am.is_ack) { // A REJect or ACKnowledge received, drop. return 1; } // If there is msgid in the message -> I need to ACK it. if (am.msgid != NULL) { ack_message(srcif, aif, pb, &am, dstname); } // TODO: Process the message ? return 1; } #endif /* * Process transmit of APRS beacons * * Note: txbuf starts if AX.25 Control+PID bytes! */ int interface_transmit_beacon(const struct aprx_interface *aif, const char *src, const char *dest, const char *via, const char *txbuf, const int txlen) { uint8_t ax25addr[90]; int ax25addrlen; int have_fault = 0; int viaindex = 1; // First via field will be index 2 char axaddrbuf[128]; char *a = axaddrbuf; dupecheck_t *dupechecker; int axlen; if (debug) printf("interface_transmit_beacon() aif=%p, aif->txok=%d aif->callsign='%s'\n", aif, aif && aif->tx_ok ? 1 : 0, aif ? aif->callsign : ""); if (aif == NULL) return 0; if (!aif->tx_ok) return 0; // Sorry, no Tx dupechecker = digipeater_find_dupecheck(aif); // _FOR_VALGRIND_ -- and just in case for normal use memset(ax25addr, 0, sizeof(ax25addr)); memset(axaddrbuf, 0, sizeof(axaddrbuf)); if (parse_ax25addr(ax25addr + 7, src, 0x60)) { if (debug) printf("parse_ax25addr('%s') failed. [1]\n", src); return -1; } if (parse_ax25addr(ax25addr + 0, dest, 0x60)) { if (debug) printf("parse_ax25addr('%s') failed. [2]\n", dest); return -1; } ax25addrlen = 14; // Initial Src+Dest without any Via. a += sprintf(axaddrbuf, "%s>%s", src, dest); *a = 0; axlen = a - axaddrbuf; if (via != NULL) { char viafield[12]; int vialen = strlen(via); const char *s, *p = via; const char *ve = via + vialen; *a++ = ','; axlen = a - axaddrbuf; if (vialen > (sizeof(axaddrbuf)-axlen-3)) vialen = (sizeof(axaddrbuf)-axlen-3); if (vialen > 0) { memcpy(a, via, vialen); a += vialen; } *a = 0; axlen = a - axaddrbuf; while (p < ve) { int len; for (s = p; s < ve; ++s) { if (*s == ',') { break; } } // [p..s] is now one VIA field. if (s == p) { // BAD! have_fault = 1; if (debug>1) printf(" S==P "); break; } ++viaindex; if (viaindex >= 10) { if (debug) printf("too many via-fields: '%s'\n", via); return -1; // Too many VIA fields } len = s - p; if (len >= sizeof(viafield)) len = sizeof(viafield)-1; memcpy(viafield, p, len); viafield[len] = 0; if (*s == ',') ++s; p = s; // VIA-field picked up, now parse it.. if (parse_ax25addr(ax25addr + viaindex * 7, viafield, 0x60)) { // Error on VIA field value if (debug) printf("parse_ax25addr('%s') failed. [3]\n", viafield); return -1; } ax25addrlen += 7; } } if (have_fault) { if (debug) { printf("observed a fault in inputs of interface_transmit_beacon()\n"); } return 1; } ax25addr[ax25addrlen-1] |= 0x01; // set address field end bit // Feed to dupe-filter (transmitter specific) // this means we have already seen it, and when // it comes back from somewhere, we do not digipeat // it ourselves. if (dupechecker != NULL) dupecheck_aprs( dupechecker, axaddrbuf, strlen(axaddrbuf), txbuf+2, txlen-2 ); // ignore Ctrl+PID // Transmit it to actual radio interface interface_transmit_ax25( aif, ax25addr, ax25addrlen, txbuf, txlen); if (rflogfile) { char *axbuf; axbuf = alloca(axlen+txlen+3); memcpy( axbuf, axaddrbuf, axlen ); a = axbuf + axlen; *a++ = ':'; memcpy(a, txbuf+2, txlen-2); // forget control+pid bytes.. a += txlen -2; // final assembled message end pointer rflog(aif->callsign, 'T', 0, axbuf, a - axbuf); } return 0; } aprx-2.08.svn593/debian/0000755000175000017500000000000012320106142013666 5ustar colincolinaprx-2.08.svn593/debian/docs0000644000175000017500000000012612005774513014555 0ustar colincolinREADME TODO PROTOCOLS LICENSE ROADMAP aprx.conf aprx-complex.conf doc/aprx-manual.pdf aprx-2.08.svn593/debian/compat0000644000175000017500000000000212021456606015077 0ustar colincolin5 aprx-2.08.svn593/debian/changelog0000644000175000017500000000041112320106142015534 0ustar colincolinaprx (2.08.593-1) unstable; urgency=low * See main ChangeLog. -- aprx maintainer Sun, 06 Apr 2014 02:06:07 +0300 aprx (0.0.0-1) unstable; urgency=low * Initial debianization. -- Kimmo Jukarainen Thu, 3 Jan 2008 20:35:29 +0200 aprx-2.08.svn593/debian/aprx/0000755000175000017500000000000012313357165014657 5ustar colincolinaprx-2.08.svn593/debian/aprx/DEBIAN/0000755000175000017500000000000012313357166015602 5ustar colincolinaprx-2.08.svn593/debian/aprx/DEBIAN/postinst0000755000175000017500000000060012313357165017406 0ustar colincolin#!/bin/sh set -e # Automatically added by dh_installinit if [ -x "/etc/init.d/aprx" ]; then update-rc.d aprx defaults >/dev/null if [ -n "$2" ]; then _dh_action=restart else _dh_action=start fi if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then invoke-rc.d aprx $_dh_action || exit $? else /etc/init.d/aprx $_dh_action || exit $? fi fi # End automatically added section aprx-2.08.svn593/debian/aprx/DEBIAN/control0000644000175000017500000000117712313357166017213 0ustar colincolinPackage: aprx Version: 2.08.580-1 Architecture: i386 Maintainer: HAM APRX release maintenance Installed-Size: 1048 Depends: libc6 (>= 2.7-1), openssl Section: hamradio Priority: extra Description: APRS Digipeater and iGate Aprx is an APRS specific Digipeater and iGate. It supports multiple KISS-TNCs on serial ports and listening to any kernel AX.25 network interfaces. . Additional features include a built-in "erlang-monitor" to analyze activity level of radio channels. . This software requires a valid (and unique) ham radio callsign to operate fully and is therefore useful mainly for licensed radio amateurs. aprx-2.08.svn593/debian/aprx/DEBIAN/md5sums0000644000175000017500000000146012313357166017123 0ustar colincolin7c2a0786fee7fee92b86115088853a62 usr/sbin/aprx d6a98f1bbd799491f9559fc43e8bf7f6 usr/sbin/aprx-stat a9d04507f86642315745ba8f0e76df32 usr/share/man/man8/aprx.8.gz 9cf04acfcca16e1c5791bcd395052b27 usr/share/man/man8/aprx-stat.8.gz 673c8ba180344e2d5c22bb75f99809ae usr/share/doc/aprx/README 09890d56e7d42c48f4bafcff6366b304 usr/share/doc/aprx/LICENSE 6a4c523ff4e1adba1aaf0b8d3325fb08 usr/share/doc/aprx/ROADMAP c994b2fce146b8ef5cda6678ca45b6f2 usr/share/doc/aprx/copyright 261f41ba62cd3667fa45400cb41c92f9 usr/share/doc/aprx/TODO.gz 70e5e4e805429add4c0fd5958722e6b3 usr/share/doc/aprx/PROTOCOLS.gz 5105fa38a3b4f732baea02a62b5c6eb9 usr/share/doc/aprx/aprx.conf.gz 02cd7c464362015dd31ea4c5e0d8fe2b usr/share/doc/aprx/aprx-complex.conf.gz 4823ba66d187982a7570635cbed33a79 usr/share/doc/aprx/aprx-manual.pdf.gz aprx-2.08.svn593/debian/aprx/DEBIAN/postrm0000755000175000017500000000025212313357165017052 0ustar colincolin#!/bin/sh set -e # Automatically added by dh_installinit if [ "$1" = "purge" ] ; then update-rc.d aprx remove >/dev/null || exit $? fi # End automatically added section aprx-2.08.svn593/debian/aprx/DEBIAN/conffiles0000644000175000017500000000014212313357165017471 0ustar colincolin/etc/apparmor.d/sbin.aprx /etc/aprx.conf /etc/logrotate.d/aprx /etc/default/aprx /etc/init.d/aprx aprx-2.08.svn593/debian/aprx/DEBIAN/prerm0000755000175000017500000000043112313357165016652 0ustar colincolin#!/bin/sh set -e # Automatically added by dh_installinit if [ -x "/etc/init.d/aprx" ] && [ "$1" = remove ]; then if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then invoke-rc.d aprx stop || exit $? else /etc/init.d/aprx stop || exit $? fi fi # End automatically added section aprx-2.08.svn593/debian/aprx/var/0000755000175000017500000000000012313357163015445 5ustar colincolinaprx-2.08.svn593/debian/aprx/var/log/0000755000175000017500000000000012313357163016226 5ustar colincolinaprx-2.08.svn593/debian/aprx/var/log/aprx/0000755000175000017500000000000012313357163017200 5ustar colincolinaprx-2.08.svn593/debian/aprx/var/run/0000755000175000017500000000000012313357163016251 5ustar colincolinaprx-2.08.svn593/debian/aprx/usr/0000755000175000017500000000000012313357163015466 5ustar colincolinaprx-2.08.svn593/debian/aprx/usr/sbin/0000755000175000017500000000000012313357163016421 5ustar colincolinaprx-2.08.svn593/debian/aprx/usr/sbin/aprx0000755000175000017500000156717312313357163017345 0ustar colincolinELF 4L4 (&#444YYY0Y00((( Qtd/lib/ld-linux.so.2GNUaq]k?Bh3jiPS\[mKl>`bFegnU7X<L1dpOfH5aN042 =I/;,!WVTACD '9Z %-#(.:*@ &) oG86MRJ^+YE_$c "Qm#&"mnp)K97<q<1`p l@\ 1elDJ)gR;h  ~+?7egBlc"?p:l]qX,IP5p{2E89F}m ]2:f78<D \4IFTjn&24i-pEt%6c/5Je>q!8y2 m+z(lc& #G%81$!wI48lCUQtstW^librt.so.1__gmon_start___Jv_RegisterClassesclock_gettimelibutil.so.1openptylibm.so.6atan2fsinfsqrtfcosfpthread_cancellibpthread.so.0pthread_attr_initrecvfrompthread_joinpthread_createpthread_attr_setstacksizepthread_setcancelstate__errno_locationforkpthread_sigmaskconnectsendwaitpidreadsendmsgrecvfcntllibc.so.6_IO_stdin_usedsocketfflushstrcpygmtime_rexitexeclsprintfsrandfopengai_strerrorregexecpipe__strdupstrncpysignalputsunlinkputcharreallocsocketpairmemchrmemrchrgetpidkill__assert_failflockstrtodstrtokstrtolmmapfeofsyslogcfsetispeedfgetscallocstrlensigemptysetopenloggetaddrinfomemsettcsetattrgetoptdup2pollsigaddsetstdoutfputcregerrormemcpyfclosemallocstrcasecmp__ctype_b_locsscanfregcompoptargstderrioctlfscanfstrncasecmpfilenofwritegettimeofdaysetlinebuftcflushstrchrfreeaddrinfosetvbufsetsidcfsetospeedmemmovestrcmp__libc_start_mainvfprintfrandomcloselogfreeGLIBC_2.0GLIBC_2.2GLIBC_2.1GLIBC_2.3GLIBC_2.3.4   = ii  ii R ii 0ii ii Mii ii ii ti ii  omp048<@DHLP T X \ ` dhlptx| !"#$%&'()*+,-./012345678 9:;<= >$?(@,A0B4C8D<E@FDGHHLIPJTKXL\M`NdOhPlQpRtSxT|UVWXYZ[\]^_`abcdefghijklUS[Tt.](X[5(%,%0h%4h%8h%<h%@h %Dh(%Hh0%Lh8p%Ph@`%ThHP%XhP@%\hX0%`h` %dhh%hhp%lhx%ph%th%xh%|h%h%h%h%hp%h`%hP%h@%h0%h %h%h%h%h%h%h%h%h %h(%h0%h8p%h@`%hHP%hP@%hX0%h` %hh%hp%hx%h%h%h%h%h%h%h% hp%h`%hP%h@%h0% h %$h%(h%,h%0h%4h%8h%<h%@h %Dh(%Hh0%Lh8p%Ph@`%ThHP%XhP@%\hX0%`h` %dhh%hhp%lhx%ph%th%xh%|h%h%h%h%hp%h`%hP%h@%h0%h %h%h%h%h%h%h%h%h %h(%h0%h8p%h@`%hHP%hP@1^PTRh^h ^QVh@+US=u@(-$X9sB$9r[]Ív'U,tt $,ÐUWVS}O5@f1ۅu:t&@f|$$fuދDfye[^_]á|$D$^$D$D$$cD$ at&$ D$D$D$ $c\0[^_]Ít&1ۉ|$D$^$ D$D$$cD$ t$D$$4@fuLt&?vt&'USd]$D$REȉ$D$3MMbD$<_ED$E)ʉT$ D$EЉD$EԉD$E؃D$ ElD$E$d[]Ít&UXuu]}D$<4$N D$} $D$or$tG$t$D$c$E|$$D$\$$ $D$$A]u}]á] t$D$c$E\$D$$p$ D$+ v'US$] t&$HD$\$$܃$[]ÍUST] D$`$u T[]Ðt&\$]D$c$ً! t€u҃)ى\$L$$T[]É'US$ED$$uMMbE)ʉu6u $[]Ðt&$cP$[]ÍD$$D|U=}~t5$D$D$ S$5f$_\$$[]átD$ S$5f$`_\$U뿍&L$qUW1VSQ81YEEEEE}D$ D$D$$tD$ D$D$$OEdEdt&D$>d\$4$;?7wۍv$@ečt&E붍E릍딍t&넍t&$4dD$$[&ID$d$_$#dD$D$d$ `$H`$`$`$ a$\a$a$a$@vC.Ẻ$蘠#n3v 'vvFEȉ$z$HD$Kd$K$D$Ɖ$"_D$kd$D$4$$$yD$D$$pdixD$$$kD$`]$D$`$D$`$D$$ D$ $0軛vcv۶$zd 11}&f$褦  D$E܉D$E؉$$P$/$7$_/$w$ϴ$w$O\$$ =$ED$0uD$<$m$U$]/$5T$譔$E$蝪$$E$$}$Ȥ#/~t$$]5'D$$uEED$D$Cj$}$E~kD$$uK$ED$bD$ D$$$$8tE쐍t&ED$D$$>D$$D$$ P$FD$~D$$AP% AA]Í AҍUD$$M ÐUWVS:uu&u$ft$znz1fztv= v=t&=t&t&Cv=vt6=%t=t&v vv vv vvvvvvvvvv vvvvufuE U@UuftڋuftɋufuE U@UufuE U@Uuftڋufug tu gunED$$m$oU$ElM $u؋U M $XU MD$L" $諜uluDED$$m$*oUD$ D$$E8M Augu{ED$$l$nUD$ D$$EEKuML$ F D$$hD$dEEug ED$$l$EnUD$ D$$ESM ۉAuu F;}='wO M A$|hD$gu guDE U@UAEUD$ B D$$fT$$|h%=t $ NE[^_]Í&UVSD$ !$@$sDD$D$螨4$CCC ǃD$N4$C[PCZKLD$ 4$D$ [^]ÍU(]u}HgU :t uYg>u]u}]ÐlgÉ$$u u >t~c~pf~$ED$$ag$iD$E$Fg$niơU$NjE$D$UT$ UD$sg$T$t&ut$u\$4$j%~g$ED$$f$hU$҉tt$D$$hj|UEt$ $iT$D$Ft&U]]u!tn!9~^)Ѕ~lD$tD$$s~O!!)Ή!uQǃ!ǃ!]u]Ívǃ!ǃ!]u]f!wt&t tt$D$$ǃ!!뵉'UWVS\5 u EwEEE EpA D$$vU$ MD$D$$諤Eu95UEtՋ5t UB MAK~QD$ $胣9}9}D$D$$ 跣 u4$莓M‹fBfB!~;!~fBEuE95E\[^_]Í&=tptgMAQu^tID$$蔣~0E\$$聣MVFQ Azt&ugFFdž!dž!D$D$ $ Uɉ6u5{]D$D$eU\$D$$dUD$$]{@t2u!tDL$$D$TD!9uE$-t&$ ɉE?U1D$!$t6D$!<$tD$!$t1D rMuEEEEEL$ t$\$<$#Et]D$D$@$Ux.$$UMBD$BD$$E$OE$UǂpB 1ǂ ǂ B  rMA@u>uD$FD$ $iD$D$%Vt&$iD$ D$D$ t&t $@iD$D$M$uD$D$$裟$D$$hitvD$ $gD$D$IM'Ë$D$$gD$nt&$gPt&pvËst&u\u$D$D$$賞=+D$$g}$D$$iD$Z넍UWVSE U}EE҉UE 1۾|$$ 9]~=UD$g<$D$V~ʼn|$$|y9]É|$ 1۾D$D$$g&9]~ZEEttR~MEB^v.|$$~|$$|9][^_]Ív밉}EE Eg[^_]Vv'UWVS,UER҉EdEf1ۅ+v9MU4;u܅x؋Ft!tvtvuD$$ 蔜4$UD$ $ D$؛9st&1x9=iMU4;uDgtˋ 9dž + dž ~PD$ D$$\=t& F Ft&&&$,F$D$D$Ϛ ~ 9)9= dž MEU9Q,1[^_]Ív4$MUD4$  뇍)t&)L$D$$E ) K)L$D$$ $F$D$D$蝙D$ $jD$D$U\$gD$D$1D$ \$ D$$$ kD$ $ M쉆 ; }= FхuK tn ti ; džp|Ådž 1dž t3 t.p=F뛍t&F뎍&pEv~Ƅ dUD$D$:$MD$ D$L$+]!Ë$D$D$p\$ D$D$ED$$ pdžpo4$_ {U1ɉW}Vu S-*}B<vB< v[^_]Ðt&U uEX1tр-t^1Ҁ*uEˀπNX9uU B1뗐U1 )ʍt&@9u돍vu1R<*tX@ED$$D$^$U}uEu9}Ew&9}쐍t&v?,ZBD$|$$$Et}~yM$kL$|1|$$jV1v5U$djT$+1M+EM+EO9Md:ZB?tE$+EDGUF9FDžt<:D$ T$\$D$k$xFN@P =u>vOF [^_]Í&)V$D$L$D$$[F)F^Fu>w~,FT$ L$D$$F|$D$ U $vt+} |$D$ M $Vt+} E _ @ F\$U D$T$$褾F^^)ÍD$\$D$$購~auhF;FF|KF1F [^_]ÅHFF5t&FPDt& 1[^_]ÍvVNBD$D$kD$ FD$D$$a'UWVS\ k1󫋅B(;$( AWBPM1 rEL$D$ BEEED$EE$蕼~ۉUkt$k$ƹQD$|$ D$BD$$pD$t\ [^_]Ð(1&t$p;Ő|عE1ۅ@uE2҉~|||||ux't&8C D$CD$C$κ‰ҿkxClD$C$D$x$lWt&U  v BdU$EtOt $p~ Gt&DŽ|d&PBD$$lD$0$QD$D$B D$ BD$+l<$D$蓶‹A@tD$D$Fl$iG ! t€uҍD$1+$7L$SDž|kLk? UX']u}D$ D$'\$$öu0=~ $q跺 ]u}]Ít&|ƄCECU ;EC ECE|uUE~| 79wtMt$|$$f&UWVS`$袴D$$蒹D$$肹D$$rD$$bD$$RD$$BD$ $2D$ $"D$\$$ڷD$$fqEEEEE +(t E$jUD$D$$zxE$jf@f@C uC ;x^=P51C@~C ;_U$i‹fBfBKtfBED$$ zE$iD$ED$E$nU҉LLBP=%U>1\JHP;uZLL;uxB&tC~S9~)ЉD$D$D$$;=~)SD$BD$ $D$$~C{;{C ;>' t*C =w \JC ;{}d uыC ~CƄ\JC uQD$ C D$PD$$_xW;{C |CCfSBD$D$lPD$ C $D$;뀐迯 thvtovtkv{f d)>D$HT$$C)CpQLBjllE$lD$UELB+RoFvl~$dlt&UD$D$$ul7E$e[^_]$Ql輳~D$ \qD$"D$l$lC&UWVSuЋMЋUċE $\JuEEȋF}@  E؅}Eq$ uD*)č\$'uT?e[^_]Í&uFD$t$D$ةe[^_]Ít&}%KEx$VUB@"tM؉L$u4$$MAD$$¬tWuЉ\$t$}|$ E؉D$$(vD$蛬UB+P$5x}$q\$rMȉL$uԉ\$ t$}GD$ED$G$Hv‹@+D$B$tD$d}GEԃEt&G&x\ ]E,t&=~ $vE}9=x!tŰuv H E܉uME$ԪUD)č\$'=~2MAD$AD$ut$ }|$E$`vD$U܉$GEM܃9u,=x~#=3$v"t&uqu7=$Pw1ɉM#}} Ex%{Uz$uF"}|$E$sOUBD$ |$D$r$% uȉt$}ԉ\$ |$UBD$ML$u܉4$-EԋUEx$‹@>D$BD$ MD$q$L$蝥<$ut$}Љ|$PD$SD$$T$ Ex$($uxe[^_]Ít&Eh=`$vKOfUȋEt&pvBD$BD$ MD$q$L$迤5$ }|$UЉT$PD$SD$$T$ 1My$&}GD$ ED$r$D$;TuFD$ }D$q$|$Qt&E@ D$UB$$ؤMA$A$@uEA(A,q0^(v $蓩%}G D$q$D$MA$tD$Nt&EЉ\$ D$U؉T$$uD$MA+P$5x}$q\$ovuFD$$¦EЉ\$D$UT$ ML$$wD$蟦uF+P$5x}$q\$vuFD$ }D$q$|$`q}Љ\$|$ED$ UT$$wD$MA+P$5x}$q\$g$qV$vfMQUA AQE$٥UD$ r$T$}uF $uD$R0MD$ r$L$>E$nE$csE$FEtD$$讣E$#D$$pd=u6y6D$D$c4$臢 $ͦx5ftD$$2$誤D$$땡D$.D$$4uD$ kut$ D$uD$$ht$D$du$JL'UWVS\54$ӟGPG $م]1E }]v9_0%)P$ }}EMش fEEm]mE ƅt+P$5x}$ r\$9_0pؐ\[^_]x}$x\$ \$]蟢GE&UWVSP0iEU,4V~+F ;} D$ $2^EM@ MEt&UEE9BUMF;uߋ=UMDt‹~,+~(|$F$F(D$F$裟F(F(D$D$ F$$'t$Tr荢럍&tFD$$odEM9 05t $r:1[^_]Ít&D$+rD$Ǡ fF(D$ D$F$$Lt>^0+F$F(BF$҉CډCFF$菠F7& ؋^0+F$F(F$҉C ډCFF$#Fv$FrF,F,D$F$$萜F$t&=t$x辠t&F=F/$lx膠$xhT$$s}3v$@]̐t&뉍E$rD$ɚ}]떐t&Exo1xr'xt&fEvt*UT$ MA D$$xD$gED$$D$lED$Kk$Euu5HuuC}vugM̉L$ uF D$$yD$EE$rD$ə}t&$]t&낍$ t&뇍}u؋]܋}EdEEM1t M $~UB <$pMUĉA1t Eĉ$VUMBEJB$B1t}E$tؘ$t̘E|$E$*tD$誘}ątU$rT$萘MAD$$0trU B D$$WM uQ^4ۉQt>$蝗$ƍ@D$ÍFD$C\$$$CEXD$D$E $ZU Be[^_]ËBB uEѣEЅtUЉ$CMA D$${D$oEtu4$͖tUB D$$0|D$1UB D$$\{D$r=$tVyE11E1EEEEEEEEEyuF$uE7U؉T$MAD$uԉt$D$ED$ UD$sT$M $诊)E܀xEȅuE7U؉T$MAD$uԉt$D$ED$ UD$sT$M $7uD$$$.EED$Z$` \$ UB D$$zD$čEMyoynvyEԅt&t'L$ uF D$$xD$bED$$?$gED$$t\$${E}]ԅ\$$9s}]uCsuЅt*ED$ UB D$$xD$訌ED$$$E$t$0{轍E ]Ћ}'\$$JsB}] uWsuzU؅t*ED$ UB D$$xD$ED$$$}]؉E\$$_s跋}]}rMyaywt&yu{D$$^$uEF\$ UB D$$xD$1EuF$sD$}u8~D$$ $EE@te\$ UB D$$xD$襊EruF$wsD${}H$諌MA6$蘌MA벋umsD$$ $GEE@ td\$ UB D$$xD$Eu~(uF( 5UB $rsD$躉}$MA 볋ulu_$D$u $E$D$ D$讇UۉB(+D$$s:}usueEx%t*UT$ MA D$$xD$EuF% ]}$sLj}]E$UBD$$s荈u؋]܋}EUWVS<]D$4$蛅EEE@ DCE&؍$8$uދUпEt$ ED$$ $ MȉE܉ $e D$u܉4$ $ uȹ EtkuȿOt ED$U܉$MMEQ .5x}$Zt\$<$X E؅=ŰB1ېt&M̋ vt6;u E 81[^_]Ãht+1duiQVʍimt&t4svt΀;u됍Mt&rk<&1t&gt&'U1ɉS]St' t- t(#t&t9 vt1  []Ív1t&Tuʀ#uǸ[]Ðt&=Iv vt vuэt&뾍&'UWVJS}GEG]G )< to:\E)ЍIGt$$D$~D3tyGD$ $}t9x Ptt&9s< u퐍t&@Pߍ&$:\r=E[^_];]uvE$}D$E[^_]Ð&UWVS }<$(E~1EvA<w -„u}1 [^_]Ívt$9tKft)ؾ뫍vA<v A< wfKu}~U|0uD8-tC& [^_]ÍvE tA< qEDPЃE~WL&U]]uD$}$~t531tMD$}$~tD$wt$}~u-t&E ]Ћu]Í{tD$$<~tD$}$(~t0t$D$}$~uE 땀{tӍD$}$}tD$g$}1҅tWUMuЍBwBuD$ D$D$}$l\{UMuЍBwB uD$ "D$D$}$l {UWVS,E0"tj'teu1t( t1 tt&0uU tU :,[^_]Ðt& t uՍPX1Et&t$82\t5EEu؄ U냍t&븃nr"'t&\&x&uE1;U$EED$D$yUUFfE E-U UE"EU'Ut&CE\E?D$ 2D$uD$}$}x'UEt%v t t fÍ&t&D$ BD$D$}$}xv'UWVSuDžDžD$4$yQDžp4$:$u؍}4$ D$l$$Ël$_D$$$l tl} >l} >l} 8l~ 2l ~ elLk3$F$*{D$$HPt$~D$HD$ D$D$x_&$"Å@Džp1$fwpĬ[^_]Ð$*붍$+랍$P댍$~ws$Tt\$t$$D$ wDžpg1ۍ&$"$u؍~4$D$l$G$oxl$xD$4$$=l |l~u{x$y#l)s;l$D$D$v\$$~vv l'~t4|x$ D$t$ D$D$vx$Nx`|x$D$t$ D$D$ul/~=x$w{a{tv{tD$$D$lD$É$*$R;lZ{oP{nE{5D$$$ tD$Z$DrD$4$,lED$ED$ ED$tD$$5v$D$D$Ztt\$$Tt$@tD$$_\$ $0D$D$sl $D$D$s$$uD$$Pdt$dD$PD$ D$D$Us,\$ $D$D$,sl&$ȃD$D$rt$t$脥]$D$D$r\$$~r0l5~5x$t$D$D$6rt$~t$ r|x$DD$t$ D$D$q=P$D$ D$D$qPl=~  t4|x$D$t$ D$D$^qx$sf$D$D$$qt$$~ql4u^$D$ D$3o$X!ËD$D$p=)$訢1lMl[ ut$hD$D$Qp5t.t\$ $gD$D$D$p$1D$Klt$܅D$D$o t.t\$ $D$D$D$ot\$1ۉ$t$Sl ll/l lstl\$$D$ D$D$n$̇D$D$nN$D$D$n$t\$ $̄D$D$D$Un$PD$D$0n$D$D$ n$D$D$m}$\D$D$mXED$ED$ ED$D$4$GoSE$CtEt~E0tt&F~1ۍ&F9EUD$T$F UD$ ET$D$$ h롉'UWVSl&MbX؉)ʋ hi)ӅɉOƅƅDžt&9 h`4^$ntĉ$dtM $\$D$qD$ 4AD$D$$A‹FlVh~N,1ۉ1Ʌ0vkD|9}Ã9}y狕ߍ&S$ۅ=PD$$ٝم Hٝم\$W@VhFl~N,1ۉ1Ʌst&k9}Ã9}y䋕܍t&S$ۅ=PD$$ٝم Hٝم\$?NhFl11ۉfk\p9}y틍k\p9|P$\$D$$?NhFlb11ۉk\t9}y틍k\t9|P$\$D$ $>NhFl11ۉk\x9~y틍k\x9P$\$D$$ >0000C0000C=)B"uzU$f/jvvJvl[^_]ÍCD$PUL$D$SD$ $D$юJt&T$A"D$ A"$D$ED$@t&D\$$@t&vUWVS<=&Mb5hX؉)i)Ӆƅ1ƅt&95h`X$it؉$8_t̡U\$D$q$D$ ;lX7u6\$D$`$;D=+G"uRU$95hl1!‰l<[^_]fCD$D$PD$ST$D$ E$艌rt&D$G"UD$ G"T$$D$>v\$D$ؑ&\$D$&L\$$,6>t&UVS=~]D$x$kD$p$UMb$ Ɖ)ډT$)T$=D$$p~RD$$x~ 1[^]ÐD$ D$x$x|1[^]ÍD$D$p$pL7닐t&U$=%D$D$$pD$xD$$xuÐ$>ɍvÐU]Ít&'Uh}} ]u8-x=5]D$<$ AE\$D$X4$D$ <‰D$D$a4$qt<,ft<:t&t t&[^_]9sӍBwy&uFʍt&-uEQtiYC<wYQٍA< e Z&9vӍBvBЃ v륐t&BЃ t&rvIA< vA<w A< vA<wA<&ЍA<v܍vu 1ҍt&MmueT$|$ D$$91t$X91=tumueT$ D$L$$_91ZU]u}E8#t} ~=ub]u}]Ðt&E uD$:D$4$:E}+}G9E 5t$ :룐t&u $ȓt$8뉍vuE D$D$Rt$ D$$qG)č\$'Wt$މT$$7 EQB<>tF<,tB<:t<9suDl $tO9Bу}E\9։Er뎍&5$9EEuk$8Zu1ۉuuuu0$8utЋuttutu>qtt&;]DExExAu߀xXtcDu]~}trt $7EE u\$)؉D$ED$ #t$$ D${bTD?$ڒ7.!$7E U(]Eu] }}uu]u}]ÍD$$)D$5۸EtFD$$35t$<$D$D$ 5]E u}]2d볉'UWVSLEM}uEEMM }؋}]EԋE؉uMЉ}]؉D$ML$ uD$Rt$}<$lEMЉEM\$ uD$t$}<$Eu; E؋MEE EML[^_]f8t}=t}Ct}#t}o}IZ}PEuȀ>>GMȃMȉ\$ uD$ $t$EuAM$L$3E$PD$3t&}ȹ#t}ȹt}ȹt}ȹtw}ȾIb}ȾPMu̍v}<:FVD$*t$ D$C,D$CL$*u E [^]*fUW1VS 5+5Et.t$C$)~E \$$u؃u [^_]Í'U]uu$tp|CC؋u]]$ *t&'US]SLCP9t$'SC9t$'C\$$w[]ÐUWVSB<;};EU=E~ $I(=+=EEU4t|$C$xNދuE}uɃ=~ $m)EU9e1[^_]Í&$=zE\$$^D$'^t&UWVS<M )ЉEEA;Du M} MԃhuЋGhEFEFED$> $$tEԉ$&UED$D$Mԉ $? ÉE13]܃=~\$ uԉt$}$Ė|$&EE4t*UT$C$0M;KHt ދu֋U̅Ẽ<[^_]fu=~ $}&C.;Eu}؍s/M9}u=~ $'M EQ tAHC4$_"=~ $(V&U B$D$M $5}É|$Uԍ@/$T$ $D;/K.U }܋BH{H} C$E9 Ẻ$c"EEUMũD$L$4$}EEU13EEċEċt2E܉D$C$BM;KHV]ċEċu΋u؅?؃L[^_]Ðt&M AE} uűG$EOjẺ$yEEủ4$Y} h}ȉE‰$mCL[}1[UD$_$}̉<$EEE hE$CLC,}̉<$EEY1M Uu ŨhMȋFhEAEAD$>$E3Ẻ$]EEED$_$EE}̉<$EEM̉ $u EhuȉE}̉<$EEmv'UE D$D$E$Í&'UWVS ~HE1UUufJN`kf1ۃl[^_]$E+E ~ߋEfH/?@6M/\B<B< B< wE1+E~MUEË$ E E+EEufNj1&t&<_t) <^&  MA<3A< (A< " $כ 1t&3UB@fB;:rMy ?euF fFPu1EܹEE9Ev*Uz,u_f8,t&O9Et&w8,89UЉv&:, ;,f9]t&w؀8,;,>ېt&1ED$ED$ E؉D$D$$ ED$E܉D$ EԉD$D$$ UNtSftnvt svoEED$ED$ M͍ED$D$ $t y$V1E1R1PE,$R1PE,$5x}RPE,$5]xLE-UUHzO~HHL Qv B vH;vHpw$pء$1)51RP,$UzO~-Eu\$$E1RP,$T1RP,$5x}]E딉1R1P,$RQ,$5x}]Ee1R1P,$RQ,$5x}T1RP,$5]E1R1P,$RQ,$5x}UEf@]ÍvUS]tuC D$$~C S1[]Í&U]u}Ep։$t11@߁C8C4w'EfCfCfs؋u]}]ÍF$C4ɐ&US]CffC~[]ËCt$qHCS4C89t$p\$$-[]Ív'UWVSD$$購~ 1[^_]D$D$$I ~E1U$UT$UD$T$ T$ $yED$UET$ D$L$U$ jMD !)čt$#T$L$4$:}E$|$)D$\$t$ D$D$TUB $ݝ1=e[^_]átE $hD$5&=~ $k$ԥ@U{E$D$+5PML$$t&M$L$yvUWVS\MEU}M؋M(EE U܋U$}ԋ},MȋM]EЉU̅ɉ}"EP4"m}M܅ۉMxE}4Et&UMB8E@EEЉD$U؉T$MԉL$}ĉ|$ẺD$ UȉT$ML$}܉<$%(bUB fC8M$L$ 5}O t;S>+=C D$ED$UT$ M D$h4$L$ < f]9K4MA8 @D$$访tkMQ ,K<sguL$T$ \$$xhD$ƉD$ t $ħ\*`f.b0A1Bf5B7 C A UT$M$L$(D$ C D$*tD$$Ƌ( B4$D$D$rƃ=~\$$wV:^B D$ML$ED$ U $D$hT$.Í = D$ D$$$_*D$ L$$T$[$ fD$ |$$|*v A&v879:ov=$PjED$U$T$O*L$|$D$U+L$ D$+D$$t$SBEfCX GT$ $#tGQ<qguT$\$ L$$xhD$ƉD$fCDuSʉft1ۅt p   T$ $跣D$ B D$ $dZǡt"D$|$$3yt&l1ҍuCDu[ADt< t  uD4$/fC 4$t$D$$KT$@Q $蟍xtt$$ND$C$k:D$$(\$ $4$oi\1ۍt&uE $D$U T$ $'5\$@Q $}  ] $D\$\D$$H \$|$$$ &D$$H$fR$ɣ< $P\1#$ӣ^t$$p] $\$vUSÃu~C D$ D$D$$xxxD$t$vxf{t\xDC fx3;|||[]Ít&C $ĩD$)jfS ōBfC벍U ]2fUWVSD$<$+EE@@df@f@ @"B"M]UEM]EEEfu4$Eu4$DuދE$^KD$É$I$BK$EHUȍED$$H$K E EEEE@0EUEtOMȍPЃ Evt&PЃ ]BUuۋM9MEEEEEEEu4$-D%E$zCuދUu$JD$E$G$IM $[Gt$$G$Iuug BD$Kk$~$EED$E$}D E$RMUT$ A D$$hD$ ]E  $E8wUD$Kk$]ȅD$$C\$ ]C D$$D$uE4$B }EP Mq0M1t&K;]t&uF0s`Dtދtϋ@ \$$(D$UB0D묍t&uEH0  uEp0 z EcvMA D$$D$HEu*g E EE$\$EEUD$$E]Ћu]Љ4$AEąu>]]$Su\$ F D$$hD$}t E$ut U$ӿEEE>Uuȉt$ B D$$,D$0뻋}H$z HUD$ B D$$دD$vuF t$"MA0tt$]$Č[^_]fH$EM]fY t$Ҥt&uBuUB0$뎐t&u u& ED$$/A&]MA D$$D$踿@&u]ȉ\$ F D$$`D$艿$:u\$ F D$$hD$NEED$$w@OU\$ B D$$D$ ofH"UD$Kk$]ȅ$h\$ ]C D$$hD$謾E4 t/U踣}u}D$ UE$T$D$g$<MƋAFAFAFAFAFAFA F A$F$A(F(A,F,A0F0A4F4A8F8]F^ \$D$`$EfFF F"MU  ЋU ЈF"UMEfV UY0$`MEt]UC0]DEЅEEЋUFV5$^MuA D$`D$4$E$D$6dUB,xM\$ A D$$(D$ļELt&H$$` ZED$Eȉ$=W]MC"ʃ ЅɈC"C L$$_<uF D$$D$F0$Evu2 #u gD$Ě$}]1EEIEU$D$x<$E譽UU2D$Ě$Qt|$\$$>#땿& 2 MED$Eȉ$+<]UC" ЈC"VFFdCED$$;U\$ B D$$D$x&ED$Eȉ$;e]UC"҃ ЈC"E̋U\$ D$B D$$\D$MUȉT$ A D$$D$EoU\$ B D$$4D$軹fMA D$$tD$蘹&UMȉB0É $褻=]HS0]̋uT$\$4$M‹A0xuE U%UM̉B0H $D$;$= ɉtD$ űE$it$D$蔸Uȉ$M̉ $UZ0D$ uM̉]ȉL$ D$sg\$F0$J|$F0D$E$‹F0xuE UEH$9EE̋UMD$B0 $D$oơ$ uM]A D$`D$$h#uF D$$D$sE]C D$$D$KEEZMUT$ A D$$D$uuP D$ D$$/EU\$ B D$$ԬD$贶vlEUȉD$$3bMAT=IMQ0BMA D$$D$3E̋U\$ D$B D$$D$Fu(HF0u]\$ F D$$ D$践ẺD$$\D$蕵MQ0Qg ED$Kk$s]ȅ$(U\$ B D$$hD$(EU]uCt$ B D$$D$Ew*g Ep0Uu4$VUȉT$ËE$D$MA0DH$躶M]A $D$`D$1u^0$/WMA"$D$>4uTuF"tK>t@U\$ B D$$4D$ҳEZH$ZMA t$$ص]uC D$`D$4$OS0t C =UB $D$GMUȉT$ A D$$D$A]ML$ C D$$8D$VMUȉT$ A D$$4D$ʲ멿 gED$Ě$qUzduBBMAۉAtt$D$$>N]CD$C$6‰C+CӉ4$)]D$Ě$ײuP uUED$ D$$!EZMUȉT$ A D$$ԬD$裱E+\$ ]C D$$D$wEUEf@ ]ÍvUS]C ffC t[]á\$$=~\$$O[]U(}E} u]1ۍt8nf~؋u]}]Í$t$D$É$a=~E\$ |$$^D$}D;i{${(D;hCX됍'U8E]]u} ~ D$D$>$u1]u}]ÍEO9v܉)؉Ew͋E4F9v9vMG<-<,t&<:ME;uvU<-K<,.<:u݋UMʉUE)ȃ Y9<,<:vUE;uvU<,t<:uME$L$$6UM f@ fPUfHFFhT$E\$$BFXM$U $L$T$)MU)])]FXN\)M$MF`E$V$҉N@MFdf)fFDhMfUF,DhMfVUF0DhF4DiFW1W 3}-t*UE}zB1z t&E0<we~u_Eˋ}E)_ut EPX } }C ]&E8<&v2}1ft]EMUPt@P } tXI~uȋ}} G-]"G 1F 9n۸Gug|$T$D$ L$t$$M荣fxus 9=$蛤fDžDž$Dž fxDž(Dž<tuF D$$D$蝂DžU\$ B D$$D$ndDž=4IMA D$$D$dDžE0UB D$$`D$跁dDž 5tx}1EEfEٝTLEٝlEfEEDž<Dž@DžDDžHDžLDžPDžXDž\E$E$>uދ $D$É$$$D$ $W$ h4g ufD$Kk $$u 5H$芩PtS=P$nt$UD$ B D$$D$oMT$ A D$$D$sDž\xD$ D$$}҉XtD$$XX Ut$ B D$$@D$~DžX Dž\ ޿_ hD$$$D$${hD$ $ٝTd{ٝlمTнwtمTv x}ٝTمlнwpمlv LٝlمTمlv مlٝTمl\$ $FمT\$}ּ ht$EJDž\wUt$ B D$$`D$)}dDžPr\D$l$4zP‰ƉHLHBEJ@B DJB$t$RwuE1D`MA D$$(D$wDž\5% t0 u1G$~ uNDžL= tu\$ L$F D$$PD$vP t t[ uDžLZf uDžL7r t݋U\$ D$B D$$PD$uDž\UM]EÉ'UM]EÉ'U]Ít&'UM]u}<-t-,911>,t#t&9j a<,u؍UEEfEED$t$$rl^u^,+]ԃ ~ EԍUEEfEE\$D$$,lM ڍE$uFE FH@r7U Br.U] BLC rU B؀Af:M fy%t& vUB$ؾlmE E @vMY ~ uEEfEEFDD$E݉\$${E E MYuEEfEEF@1u F;8u f~ uv!$lE E ]M1۾/yU^=$GlE E UM BLD$BPD$BHD$ A D$AD$A$tT$$H]jEً] C;U fz&rD$ÉD$B($kM A&D$ÉD$A$$kE1EfEEEu @V.F;F]U fz nMALD$APD$AHD$ B D$BD$B$EEE fx&vE$t$\$$Mԍ}A\ 1GfGE&ZUԋEfP U܅MEfA A$7eỦ$*eD=$CfM̉t$D$@lL$ É$aEP09t$cUZf=`<[^_]Íuu$$M E@|K1ہ<[^_]Ít&} E fDžX7ƅtfDžfDž/GW u1ɿ]؉U"E܉CCqtVtV{qtS+E+U܅~ȋU؉DUT$$3Zu,[^_]Ðωut&1tET8EJBEAEfAB,1[^_]ÍD)&'UWVS=`\X\1\fE/>E dB dEGEE vs9~A>[TD$E$!Yʍ{9t&ōEHH1L׉P󫋅X:tRU1]t=f<,t u<,1ƍ\ AQuXW:tYL1Lt>t&<,t u<,t\1ƍL AQuTt<8At15[^_]Et&뜀xuɋEt81xX8}1&FZ\~h< uF-\| uʃʼnэy ƒu~|9׉Lhh>APDBPRS,BDSTAfB R*hlG:G!uMD$.4$Qe){0~$t$D$ZPW99v 9whEut$D$$PpEpEuD$.4$Pp)0~$t$D$OW9v9ډvf 9wp }E$D$D$OOE{igMtcҍ|U>t u>tP@<:t@<,t&$<:vt<,t&&u֋L5}T$D$ L$)؉D$|D$`$1}at&d}TZD$<$P& }/]&AD$ D$\$|$PP\UD$$PLB8 $HQh8-__9vD$$P[^_]9 _r9v<*u91 &|T$$O($t$D$KXUd4|X[\PD$ D$$M1Xa$xlPPv@t$$N+t$$N X`CUSÃt1CtC[]ÐC $OCC$N븉'U(uu }}]tBD$or$Mt,t$ |$D$\$pNu]}]] M]u}]ÍvUWVSLE D$ pD$D$@ $M! FM 9ЉMM]} è ǡ M܉]؉}ԋE$]E=} } &}$MpwE̋U Cp눋]!}pD$$CD$Eԉ$kNW])Å[MЉ\$D$ $K}Ƅ pt}о_}о.cE]p'Ƅ Ut&U!-M}ԋpD$$<$CD$M])ÅUMЉp\$D$ $J}Ƅ Epv}йuU ,}о.`MEp4~1vƒ19E *u9E*9U: *ED$EЍML$ D$2$RLE9e \$D$$lJv$XJt&h}оEŰ!;Sv}̋UЉ$|$T$]I8t$D$IEǀpUEpMЉD$$L$It&ŰMT$T$ p$D$D$_I9UhfUpFƂ $ǂp ,T<U1 1<*EuFt&] <*Et}1;p|ىEЍUMT$ L$D$K$IJE95\$D$$hdHxvpEƄ M܃ D$$L$}؋pށƄ UЍED$D$#$Itf=EMЋ]t$ D$L$T$p$`D$GfL[^_]Ë}Љ|$ $C;uu=f~\$t$$fGt3E}ЋUt$ D$|$D$p$D$)GM]pɉM]$}й}]̋ED$: \$$HU!UM܉}+ED$L$D$\$D$ D$G $D$ D$D$G $E>!>t u>2tJ@<:@t:<,.<: t<,t&&u؋E܍VT$L$MD$ )ЋU܉D$ $T$q]p~ =uw}Ƅ p~ uHU!ǂpLJp1FU1ǂp2EЉ4$D$)L$T$$ Dph$F]$:\$D}p11 b}$g|$xDJ}оEŰ!;S 4C}̋UЉ$|$T$CC80C$D$ D ]$\$CD$$A@}!$ЃD$$@MpCduؿQ==]$g\$SC%M ,tUI}$|$"C1EЃD$C$@MCpC }uˋ!ƃ UЋEAU11VuS] ~"t&2f19u[^]Í&U11V1ҋuS] ~(v2f19u1f‰[^]Í'UVES]u t"1ɍv2f19u[^]Í&UW}Vu S1ۅtŃ1f39u[^_]ÐU$DD$$=ÍUWVSL=~$D$EA1}:t&=~C+t$$D$A95^HD$C$ 1 rED$ |$CEEED$EE$?tj=~0`<$@D$CD$ t$$$D$E@E4$<95,&L[^_]Ã=~CD$ t$$TD$?]GEK,P@ $T$D$?UC BCBCB CBCBC$/<,CU$ e>D$$ q=D$ D$`ZD$ $-@ US|$;D$$@D$$?D$$?D$$?D$$?D$$?D$ $?D$ $?D$\$$L>D$$@ u-fD$0uD$$ ?tՁĔ[]ÍUWS$<>1EE CC,C D$$:+ډ[_]ÐUU SER++Hy ʸC҃)i@B@BȺMbi[)ʍ]Ít&'UE S]9t @CiMK?B~ȺC)i@B)щK[]ÍvUME 9t @AE]fU1҉E+E t ЍT]fUMU +tЍT]ËB9A|t&'U(uEu M}}]EEM E)tH~K9)Ѓtt&~]u}]Ð;^}uAEM]uE}]f;^~ u9E$U 뒉|$ D$M$L$;뢍v|$ D$M$xL$;몐U]Ít&'UWVSO 6)t$1ED$E D$E$9rރ [^_]Ë$ÐUStfЋu[]ÐUS[l=Y[Sorry, unknown erlang syslog facility code name: %s, not supported in this system. %04d-%02d-%02d %02d:%02d:%02d.%03dMONOTONIC TIME JUMPED BACK BY %g SECONDS. ttcallcount=%d MONOTONIC TIME JUMPED FORWARD BY %g SECONDS. ttcallcount=%d mypid=%d aprx: [-d[d[d]]][-e][-i][-v][-L][-l logfacility] [-f %s] -f %s: where the configuration is -v: Outputs textual format of received packets, and data on STDOUT. -e: Outputs raw ERLANG-report lines on SYSLOG. -i: Keep the program foreground without debugging printouts. -l ...: sets syslog FACILITY code for erlang reports, default: LOG_DAEMON -d: turn debug printout on, use to verify config file! twice: prints also interaction with aprs-is system.. -L: Log also all of APRS-IS traffic on relevant log.Seen configuration errors. Aborting! APRX: NO GLOBAL MYCALL= PARAMETER CONFIGURED, WILL NOT CONNECT APRS-IS (This is OK, if no connection to APRS-IS is needed.) APRX: PIDFILE '%s' EXISTS, AND PROCESSID %d INDICATED THERE EXISTS TOO. FURTHER INSTANCES CAN ONLY BE RUN ON FOREGROUND! Could not lock pid file file %s, another process has a lock on it. Another process running - bailing out. Failed to lock pid file %s: %s Accepted list is:aprx%s SIGNAL %d - DYING! Initializing MONOTONIC time/etc/aprx.confNONE version: %s aprx: %s def:hiLl:vV?wCOULD NOT OPEN PIDFILE: '%s' %ld /dev/nullAPRX startAPRX28/var/run/aprx.pid2.08r580LOG_DAEMONLOG_FTPLOG_LPRLOG_MAILLOG_USERLOG_UUCPLOG_LOCAL0LOG_LOCAL1LOG_LOCAL2LOG_LOCAL3LOG_LOCAL4LOG_LOCAL5LOG_LOCAL6LOG_LOCAL7`P@0 dp`dddXd0ddd@dddd eee*ezD .. param='%s'xorsumxkissbpqcrcflexnetsmackcrc16pollcallsignaliastncidpollmillistnc2dprsinitstringinitstring len=%d Bad mode keywordBad tty-name/paramserialtcp!%s!%s!Bad ttyparameterstcp!%ld TTY %s OPEN - fd=%d - errno=%d (%s) - FAILED, WAITING %d SECS OK%02x = %ld TTY %s: read() frame: %s:%d TNCID value not in sanity range of 0 to 15: '%s'%s:%d POLLMILLIS value not in sanity range of 1 to 10 000: '%s' .. pollmillis %d -- polling interval %s:%d ERROR: Unknown sub-keyword on a serial/tcp device configuration: '%s' .. new style serial: '%s' '%s'.. .. new style tcp!: '%s' '%s' '%s'.. %ld ERROR: TCSETATTR failed; errno=%d socket connect() preparing: %s ttyreader socket connect call failed: %d : %s %ld Read timeout on %s; %d seconds w/o input. fd=%d %ld.%06d .. defining %d ms KISS POLL %ld TTY %s EOF - CLOSED, WAITING %d SECS -%dax25_format_to_tnc() len=%d Ax25FmtToTNC2: Bad source address; SSID-byte=0x%02x Ax25FmtToTNC2: Bad destination address; SSID-byte=0x%x Ax25FmtToTNC2: Bad via address; addr='%s' SSID-byte=0x%x Ax25FmtToTNC2: Found %d via fields, limit is 8! $mycallpasscode14580sctpdtlsAPRSIS pthread_create() OK!CLOSE APRSIS %s:%s %s%s,qA%c,%s:<< %s:%s << reconnectaddress resolution failurefail on connectsocket formation failedconnection failedCONNECT APRSIS %s:%suser %s pass %s vers %s %s filter %saprsis_runthread()aprsis_prepoll_()time_reset!heartbeat timeoutaprsis.capp.polls != ((void *)0)aprsis_postpoll_() cnt=%d postpoll_ POLLERRpostpoll_ POLLHUP>> %s:%s >> postpoll_ EOF%s:%d: INFO: LOGIN = '%s' '%s' %s:%d: INFO: PASSCODE = '%s' '%s' %s:%d INFO: SERVER = '%s' port='%s' is not supplying valid TCP port number, defaulting to '14580' %s:%d: INFO: SERVER = '%s':'%s' %s:%d: ERROR: HEARTBEAT-TIMEOUT = '%s' - bad parameter' %s:%d: INFO: HEARTBEAT-TIMEOUT = '%d' '%s' %s:%d: INFO: FILTER = '%s' --> '%s' %s:%d: ERROR: Unknown mode keyword in block: '%s' %s:%d: ERROR: Unknown configuration keyword in block: '%s' %s:%d ERROR: This block does not define server! %s:%d WARNING: This block does not define passcode! %s:%d WARNING: Your beacons and RF received will not make it to APRS-IS. aprsis_comsockread(fd=%d) -> i = %d APRS-IS coms subprocess socket EOF from main program side!***** NO APRSIS SERVER CONNECTION DEFINED *****aprsis_start() PTHREAD socketpair(up=%d,down=%d) FAIL - APRSIS-LOGIN not defined, no APRSIS connection!FAIL - Connect to %s:%s failed: %s - errno=%d - %sIn time_reset mode, no touching yet!Upstream fd read resulted eof status.%ld << %s:%s << write() rc= %d aprsis_mainmatched child exit, pid=%d %02d%02d%02dhFailed to exec file %sNothing to beacon now.APRSIS%s>%s,%s,TCPIP*%s>%s,TCPIP* next beacon in %.2f minutes %s*,%s%s*beacons offset: %.2f minutes revents of exec_fd = 0x%x msg_exec_readno newline in exec read dataSeen EOF on exec-readbeacon_postpoll()BEACON parameters: interface '%s' srccallsrccall '%s' dstcalldestdstcall '%s' via '%s' name '%s' item '%s' ;object '%s' type '%s' $myloclat '%s' lon '%s' symbolsymbol '%s' commentcomment '%s' execexec file '%s' timeout %d timefixtimefix ASSUMING raw '%s' %s%s%c%s%c%s%s111111z%s%c%s%c%s;%-9.9s*111111z%s%c%s%c%s)%-3.9s!%s%c%s%c%sRFONLYRF+NETNETONLY BEACON FOR ***>%s%s>%s' file %s ' '%s' cycle-sizeBeacon cycle size: %.2f beaconbeaconmodebothradioBEACON: idx=%d, nexttime= +%d sec BEACON ERROR: Failed to load anything from file %s Failed to load anything from beacon file %sexecing child pid %d, file: %s child process: Failed to open file: /den/null child process: Failed to execute: %s BEACON ERROR: Failed to exec file %s CONFIGURATION ERROR: Beacon with source callsign APRSIS. Skipped!%ld Now beaconing to APRSIS %s '%s' -> '%s',%ld Now beaconing to interface[1] %s(%s) '%s' -> '%s',Beacon: aif=%p callsign='%s' src='%s' bm->dest='%s' bm->via='%s' Not a beaconable interface, skippingNo callsign on interface interface, skippingBeaconing to APRSIS interface ignored in presence of other interfaces. Skipping.CONFIGURATION ERROR: Beaconing with source callsign APRSIS! Skipping.%ld Now beaconing to APRSIS %s(%s) '%s' -> '%s',%ld Now beaconing to interface[2] %s(%s) '%s' -> '%s',beacons cycle: %.2f minutes, increment: %.2f minutes Killing overdue beacon exec subprogram pid %d found newline in exec read data.. calling beacon_it() on buffer: %s .. nothing read from exec pipebeacon_now - still an exec under way.%s:%d ERROR: Double definition of %s parameter %s:%d ERROR: beaconmode APRSIS is incompatible with beaconing to designated interface ('%s %s') %s:%d ERROR: beacon interface '%s' that is not a TX capable interface. %s:%d ERROR: beacon interface '%s' that is not a known interface. %s:%d ERROR: Double definition of type parameter %s:%d Sorry, packet constructor's supported APRS packet types are only: ! = / @ ; ) %s:%d ERROR: $myloc has not been defined. %s:%d ERROR: Latitude input has bad format: '%s' Longitude input has bad format: '%s' Symbol code lenth is not exactly 2 chars%s:%d Note: Lacking 'interface' keyword for this beacon definition. Beaconing to all Tx capable interfaces + APRSIS (mode depending) %s:%d .. BEACON definition failure; symbol parameter missing or wrong size %s:%d .. BEACON definition failure; lat(itude) parameter missing or wrong size %s:%d .. BEACON definition failure; lon(gitude) parameter missing or wrong size %s:%d ERROR: Unknown beaconmode parameter keyword: '%s' %s:%d ERROR: Unknown block config keyword: '%s' set %d defined with %d entries pB?MbPConfig line: '%s' trueyesfalsenooffconfig.cY != ((void *)0)aprxlogdprslogrflogpidfileerlangfileerlang-loglevelerlanglogerlang-log1min%s:%d: INFO: ERLANG-LOG1MIN %s:%d: MYCALL = '%s' '%s' .. 'lat' missing, got: '%s' .. 'lon' missing, got: '%s' got lat: '%s' got lon: '%s' %2d%5f%c,%3d%5f%c,aprsis-loginaprsis-serveraprsis-heartbeat-timeoutaprsis-filterax25-rxport%s:%d: AX25-RXPORT '%s' '%s' %s:%d: RADIO = %s %s.. ax25-deviceserial-devicetcp-deviceERROR: Can not open named config file: '%s' -> %d %s %s:%d: INFO: APRXLOG = '%s' '%s' %s:%d: INFO: DPRSLOG = '%s' '%s' %s:%d: INFO: RFLOG = '%s' '%s' %s:%d: INFO: PIDFILE = '%s' '%s' %s:%d: INFO: ERLANGFILE = '%s' '%s' %s:%d: INFO: ERLANG-LOGLEVEL = '%s' '%s' %s:%d: INFO: ERLANGLOG = '%s' %s:%d: ERROR: Unknown keyword: '%s' '%s' %s:%d: MYCALL = '%s' value is OK for APRSIS login, and Rx-IGate, but not valid AX.25 node callsign. %s:%d: MYCALL = '%s' value is not valid AX.25 node callsign, nor valid for APRSIS login. %s:%d: myloc parameters wrong, expected format: 'myloc' 'lat' 'ddmm.mmN' 'lon' 'dddmm.mmE' got parse-field-count: %d on '%s' .. lat expected N/S tail, got: '%c' .. lon expected E/W tail, got: '%c' %s:%d: MYLOC LAT %8.5f degrees LON %8.5f degrees %s:%d WARNING: Old-style top-level 'aprsis-login' definition, it should be inside group tags. %s:%d: APRSIS-LOGIN = '%s' '%s' %s:%d: APRSIS-LOGIN = '%s' value is not valid AX25-like node' %s:%d WARNING: Old-style top-level 'aprsis-server' definition, it should be inside group tags. %s:%d: APRSIS-SERVER = '%s':'%s' %s:%d WARNING: Old-style top-level 'aprsis-heartbeat-timeout' definition, it should be inside group tags. %s:%d: APRSIS-HEARTBEAT-TIMEOUT = '%d' '%s' %s:%d WARNING: Old-style top-level 'aprsis-filter' definition, it should be inside group tags. %s:%d WARNING: Old-style top-level 'ax25-rxport' definition. See groups, 'ax25-device' definitions. %s:%d WARNING: Old-style top-level 'radio' definition. See groups, 'serial-device' or 'tcp-device' definitions. %s:%d ERROR: The 'ax25-device' entry must be inside an group tag. %s:%d ERROR: The 'serial-device' entry must be inside an group tag. %s:%d ERROR: The 'tcp-device' entry must be inside an group tag. %s:%d ERROR: The 'beacon' entry must be inside a group tag. %s:%d: ERROR: Unknown config keyword: '%s' '%s' %s:%d: Perhaps this is due to lack of some surrounding tag ? config_STRUPPERconfig_STRLOWERconfig_SKIPTEXTconfig_SKIPSPACEnetax25_sendto() tx_socket = -1, can not do..netax25_sendto() ifindex < 0, can not do..netax25_sendto() the sendmsg len=%d rc=%d errno=%d Can not create socket(PF_FILE,SOCK_DGRAM,0); errno=%d Can not open /proc/net/dev for reading; errno=%d Compating netax25_devs[] i=%d callsign=%s netax25rx packet len=%d from rx_socket; family=%d protocol=%x ifindex=%d hatype=%d pkttype=%d halen=%d .. not from known AX.25 device.. not from AX.25 device configured for receiving.Received frame of %d bytes from %s: %s %s is ttyport which we serve. %s ax25_to_tnc2(%s,len=%d) rejected the message: netax25_openpty('%s') failed to parse the parameter string as valid AX.25 callsign. Not opening kernel pty. openpty() rc=%d name='%s' master=%d slave=%d aprx: Could not open socket(PF_PACKET,SOCK_RAW,ETH_P_AX25) for listening. Errno=%d (%s) -- not a big deal unless you want to receive via AX.25 sockets. aprx: Could not open socket(PF_PACKET,SOCK_RAW,ETH_P_AX25) for sending. Errno=%d (%s) -- not a big deal unless you want to send via AX.25 sockets. netax25_sendax25(%s,len=%d) wrote %d bytes netax25_sendto() len=%d,%d // /proc/net/devnetax25.capp->polls != ((void *)0)netax25_openpty() error exit.netax25_sendax25() len=%d netax25_postpoll%s %s %ld %s %ld %sRXDROPTXerlang_add(%s, %s, %d, %d) /var/run/aprx.stateERLANG%-2d %s Rx %6ld %3ld Dp %6ld %3ld Tx %6ld %3ld : %5.3f %5.3f %5.3ferlang_timer_init() to be calledD Aaprxpolls.capp->pollsaprxpolls_newtransmittransmittersource .. source_aif = %p telemetry_prepoll()T#%03d,%.1f,%s (to is=%d rf=%d) %s telemetry_start()%s:%d ERROR: This transmit interface has no TX-OK TRUE setting: '%s' %s:%d ERROR: Unknown interface: '%s' %s:%d ERROR: Double definition of 'via' %s:%d ERROR: 'via' keyword without parameter %s:%d ERROR: The 'via %s' parameter is not acceptable AX.25 format %s:%d source = '%s' %s:%d ERROR: Digipeater source '%s' not found %s:%d ERROR: Unknown block keyword '%s' ERROR: Failures on defining block parameters APRS RF-Telemetry will not be activated.Defined to transmitter %s Telemetry Tx run; next one in %.2f minutes Telemetry LabelTx run; next one in %.2f minutes :%-9s:PARM.Avg 10m,Avg 10m,RxPkts,IGateDropRx,TxPkts:%-9s:UNIT.Rx Erlang,Tx Erlang,count/10m,count/10m,count/10m:%-9s:EQNS.0,0.005,0,0,0.005,0,0,1,0,0,1,0,0,1,0telemetry_postpoll() telemetrytime=%ds labeltime=%ds AHCB?%s %-9s %c LenientStrictNot relayable packet! [1]RXTLM-Not relayable packet! [2]TCPXXNOGATEqAXNot relayable packet! [3]Not relayable packet! [4]Not relayable packet! [5].. igate from aprsisTCPIP%ld %-9s %s WIDERELAYTRACEN0CALLNOCALLcallsign scanner ran over end of buffer%s callsign format verify got bad character: '%c' in string: '%.20s' APRSIS dataframe length is too large! (%d) APRSIS dataframe does not have ':' in itAPRSIS dataframe too short to contain anythingTNC2 forbidden source stationid: '%.20s' TNC2 bad address format, expected '>', got: '%.20s' TNC2 forbidden (by REGEX) destination stationid: '%.20s' TNC2 via address syntax bug, wanted ',' or ':', got: '%.20s' TNC2 forbidden VIA stationid, got: '%.20s' TNC2 address parsing did not find ':': '%.20s' Will not relay packets where payload begins with '?'/tmp/.-%d-%s-%d.mmaphistorydb_lookup(%*s) -> i=%d .. hash match .. key match .. and not too old .. no match%ld %d %d %f %f historydb_cleanup() drop(%p) i=%d .. done. .. objects not interested .. items not interested .. other not interested key='%*s' i=%d .. dropping old record .. found matching hash .. found matching key!historydb .. inserting new history entry. position out of range: lat %.3f lng %.3f position ok: lat %.3f lng %.3f.. bad destcall characters in posits 1..3.. bad destcall characters in posits 4..6..bad symbol table entry on column 8..bad pos-ambiguity on destcall invalid object kill character invalid object timestamp character: '%c' object name has unprintable characters item name has unprintable characters item name ends with neither ! or _parse_aprs_compressed too shortparse_aprs_uncompressed%2u%2u.%2u%c%c%3u%2u.%2u%c%c sscanf failedparse_aprs_mice: %.*s.. bad destcall length! destcall='%6.6s' passed dstcall format check..bad infofield column 1..bad infofield column 2..bad infofield column 3..bad infofield column 4..bad infofield column 5..bad infofield column 6..bad infofield column 7 passed info format check%2u%2u%2u.. posambiguity code BUG!GPGLL,GPWPL,PNTS,1,body too shortGPGSA,GPVTG,GPGSV,Unknown NMEA: '%.11s' %.*s%2d%f,%c,%3d%f,%c,GPSSPCSYMNWS-NWS_SKY:PARM.:UNIT.:EQNS.:BITS.parse_aprs_object object has empty nameobject name: '%.*s' no valid position in objectparse_aprs_item item name has invalid length no valid position in itemparse_aprs_telemu   H     &          Hލˍ&7&                   !          H                          ehyV-C6-C6?yV@q= ףV(\V(\V@q= ףV@B44C9HE?DUPECHECK ALLOC ERROR!dupecheck addrlen="%d" > 18, discard packet %ld.%06d TTY %s tncid %d: Sending KISS POLL kiss_kisswrite(->%s, axlen=%d)NOTE: Write to non-open serial port discarded.WARNING: WRITING KISS FRAMES ON SERIAL/TCP LINE OF NO KISS TYPE IS UNSUPPORTED! .. put %d bytes of KISS frame on IO buffer .. %d bytes of KISS frame did not fit on IO buffer %ld TTY %s: Bad CMD byte on KISS frame: ALERT: Looks like received KISS frame is a FLEXNET with CRC!ALERT: Looks like received KISS frame is a SMACK with CRC!%ld TTY %s: Bad TNCID on CMD byte on a KISS frame: %02x No interface configured for it! %ld TTY %s tncid %d: Received FLEXNET frame with invalid CRC %04x: %ld TTY %s tncid %d: Received bad BPQCRC: %02x: %ld TTY %s tncid %d: Received OK BPQCRC frame %ld TTY %s tncid %d: Received SMACK frame %ld ... marking received SMACK %ld TTY %s tncid %d: Received SMACK frame with invalid CRC: %ld TTY %s tncid %d: Expected SMACK, got KISS. %ld TTY %s tncid %d: Sending SMACK activation probe packet %ld TTY %s: Bad CMD byte on expected SMACK frame: %02x, len=%d: %ld TTY %s: Too long frame to be KISS: kiss-encoded: serial_sendto() len=%d,%d: tx null-device: %s null_sendto() len=%d,%d S==P too many via-fields: '%s' FAILno-matchACCEPTsource filtering result: %s ,TCPXX*%*s%s>APRS,TCPIP*%s>%s:??ax25hdr }%s>%s,%s,%s*: tnc2addr = %s ## process source filteracceptindifferentfilter says: %d (%s) REJECTED!filters say: send!Send to digipeatertx-oktelem-to-istelem-to-rf n=%d alias='%s' igate-group%s:%d: AX25-DEVICE '%s' '%s' null-device%s:%d: NULL-DEVICE '%s' '%s' callsign= '%s' .. store tty subinterfaces .. store other interfaceinterface_transmit_ax25(aif=%p[%s], .., axlen=%d) interface_transmit_beacon() aif=%p, aif->txok=%d aif->callsign='%s' parse_ax25addr('%s') failed. [1] parse_ax25addr('%s') failed. [2] parse_ax25addr('%s') failed. [3] observed a fault in inputs of interface_transmit_beacon()interface_receive_ax25() no receivers for source %s interface_receive_ax25() from %s axlen=%d tnc2len=%d .. parse_aprs() rc=%s type=0x%02x srcif=%s tnc2addr='%s' info_start='%s' interface_receive_3rdparty() aif=%p, aif->digicount=%d .. data does not fit on ax25buf .. data does not fit on tnc2bufpbuf_new() returned NULL! Discarding!.. parse_aprs() rc=%s type=0x%02x srcif=%s tnc2addr='%s' info_start='%s' ## produce 3rd-party frame for transmit:data does not fit into ax25buf: %d > %d data does not fit into tnc2buf: %d > %d historydb_insert_heard(APRSIS) v=%p No history entry for receiving call: '%s' DISCARDING. History entry for receiving call '%s' from RADIO is recent enough. KEEPING. History entry for sending call '%s' from APRSIS is too new. DISCARDING. History entry for sending call '%s' from RADIO is too new. DISCARDING. DISCARDED! (filter_discard=%d, discard_this=%d) interface_store() aif->callsign = '%s' %s:%d ERROR: on bad type of entry. %s:%d ERROR: block keyword: %s %s:%d ERROR: MUST define CALLSIGN parameter! Defining callsign=%s txok=%s %s:%d ERROR: Only single device specification per interface block! %s:%d ERROR: Failed to open this AX25-DEVICE: '%s' .. new style serial: '%s' '%s'.. tncid=0 %s:%d: ERROR: TX-OK 'TRUE' -- BUT PREVIOUSLY SET CALLSIGN IS NOT VALID AX.25 ADDRESS %s:%d ERROR: Bad TIMEOUT parameter value: '%s' accepted range: 0 to 1200 seconds. %s:%d ERROR: The CALLSIGN parameter on transmit capable interface must be of valid AX.25 format! '%s' %s:%d ERROR: Unknown config entry name: '%s' %s:%d ERROR: The $MYCALL callsign (%s) exists already on another interface. .. store interface[%d] callsign='%s' pbuf_free(%p) pbuf_alloc(%d,%d) -> %p digipeater_prepoll() run tockenbucket_timerstry_reject_filters(fieldtype=%d) - CODE BUG this via field is not matching with a TRACE, WIDE, ALIAS or INTERFACE.Parse_tnc2_hops rejected this.DIRECTONLY -mode, and packet is probably not direct heard. - Data body regexp filters reject No remaining hops to execute. Packet exceeds digipeat limits FIXALL TRACE overgrows the VIA fields! Dropping last of incoming ones. TRACE overgrows the VIA fields! Discard.TRANSMITTER SOURCE CALLSIGN RATELIMIT DISCARD.TRANSMITTER RATELIMIT DISCARD.%ld LEAVE VISCOUS QUEUE: dupe=%p pbuf=%p digipeater_receive() from %s, is_aprs=%d viscous_delay=%d digipeater_receive() - dupecheck_pbuf() allocation error, packet discardedSeen this packet %d times (delayed=%d) .. discard dupe record, process immediatelySeen this packet %d times. Discarding it. %ld ENTER VISCOUS QUEUE: len=%d pbuf=%p %s:%d ERROR: Unknown keyword inside %s subblock: '%s' %s:%d ERROR: Bad RE target: '%s' must be one of: source, destination, via %s:%d ERROR: Expected RE pattern missing or a NUL string. %s:%d ERROR: Bad POSIX RE input, error: %s %s:%d ERROR: This transmit interface is being used on multiple s as transmitter: '%s' %s:%d ERROR: definition failed! %s:%d ERROR: Double definition of ! %s:%d ERROR: definition failed! %s:%d ERROR: Double definition of ! %s:%d ERROR: Bad value for viscous-delay: '%s' %s:%d ERROR: Too large value for viscous-delay: '%s' %s:%d ERROR: via-path parameter is available only on 'source APRSIS' and 'source DPRS' cases. %s:%d ERROR: via-path parameter is not valid AX.25 callsign: '%s' %s:%d ERROR: msg-path parameter is available only on 'source APRSIS' and 'source DPRS' cases. %s:%d ERROR: msg-path parameter is not valid AX.25 callsign: '%s' %s:%d ERROR: double definition of block. %s:%d ERROR: double definition of block. %s:%d ERROR: Error at filter parser. %s:%d ERROR: Digipeater 's %s did not recognize: '%s' %s:%d ERROR: Missing or bad 'source =' definition at this group. Seen errors at definition. .. definition returning %p %s:%d ERROR: definition failed %s:%d ERROR: Unknown config keyword: '%s' %s:%d ERROR: Digipeater defined without transmit interface. %s:%d ERROR: Digipeater defined without :s. %s:%d Two s on this definition use same : '%s' ERROR: Config fault observed on definitions! MYCALL hops count of buffer: %s - Src filters reject - Dest filters reject - ViaField[%d]: '%s' - Via filters reject - Tx match reject - Tx match accept!DIGIPEATFAULT req=%d,done=%d [%s,%s,%s] .. discard. VIA on %s! out-hdr: '%s' data='Done.SOURCE RATELIMIT DISCARD.. discardedSeen this packet %d times. .. direct to processingmaxreqmaxdonekeysdestination .. ratelimit %f %f srcratelimit .. srcratelimit %f %f %s:%d source = '%s' viscous-delay viscous-delay = %d regex-filtervia-pathvia-path '%s' msg-pathmsg-path '%s' .. OK filter %s relay-typerelay-formatdigi-modedigipeatdigipeateddirectonlythird-party3rd-party sourcecount=%d x@{Gz? filter_match_on_callsignset(ref='%s', keylen=%d, filter='%s') filter_process_one() type=%c '%s' f-filter: no position -> return 0f-filter: no history lookup result (%*s) -> return 0 f-filter: no history lookup result (numnames == 0) -> return 0o-filter: packet type not OBJECT nor ITEMo-filter: object/item name length < 1 at the packetFilter definition of '%s' has no prefixes defined. Wild-card matching not permitted, yet filter definition says: '%s' Too long callsign string: '%s' input: '%s' The M/radius_km filter requires top-level myloc definition. It doesn't exist. .. reflen=0x%02x r2='%s' f-filter: r=%.1f km abdefmopqrstuABDEFMOPQRSTUBad filter code: %s /%f/%f/%f/%f%c%cBad filter parse: %sBad filter latN value: %sBad filter lonW value: %sBad filter latS value: %sBad filter lonE value: %s/%9[^/]/%f/%f/%f/%fBad filter lat value: %sBad filter lon value: %sBad s-filter syntax: %sBad t-filter syntax: %s/%9[^/]/%f%cBad t-filter parse: %s,o,h)p+h).+-h)h)h)h)h)h)h)H-*h)`*))(h)h)h)h)h)h)h)h)h)h)h),o,h)p+h).+-h)h)h)h)h)h)h)H-*h)`*))(86:66:6 ;6:6:6:6:6:69:666:6@:;66:6:6:6:6:6:6:6:6:6:6:686:66:6 ;6:6:6:6:6:69:666:6@:;66=88888888<888888888888888<88888m=888`=S=F=9=+=8==<8888888888888<88888m=888`=S=F=9=+=8==<889RFߑ?C jJ@QfQf@ DPRS: ident='%s', GGA='%s', RMC='%s' Invalid DPRS $GPRMC packet (validity='%s') Invalid DPRS $GPGGA packet (validity='%s') No DPRS $GPRMC nor $GPGGA packets available.dprsgw: neither GGA nor RMC coordinates available, discarding!dprsgw: invalid format NMEA North coordinate: '%s' dprsgw: invalid format NMEA East coordinate: '%s' dprsgw: previous data is %d sec old, discarding its state: %s Bad DPRS APRS CRC: l=%d, i=%d, %04x/%04x vs. %s $$CRC DSTAR=%04x CCITT-X25-FCS=%04x Good DPRS APRS CRC: l=%d, i=%d, %04x/%04x vs. %s Bad DPRS $GP... checksum: %02x vs. %02x Bad DPRS identification(?) packet - length(%d) != 29 || line[8] != ',': %s Bad DPRS IDENT LINE checksum: %02x vs. %02x OOPS! NO ON DPRS SERIAL PORT! BUG!/A=%06dDPRSGW GPS data: %s DSTAR*DPRS %ld %3d %02X '%c' Too short a line for DPRS$$CRC$$CRC%04x,$GP*%02x%cDPRS NMEA: '%s' *%x%c, DPRS IDENT LINE OK: '%s' Bad DPRS packet! %s $GPGGA,DPRS GGA: %s $GPRMC,DPRS RMC: %s Unrecognized DPRS packet: %s $$CA0\0A1\1A2\2A3\3A4\4A5\5A6\6A7\7A8\8A9\9AA\AAB\BAC\CAD\DAE\EAF\FAG\GAH\HAI\IAJ\JAK\KAL\LAM\MAN\NAO\OAP\PAQ\QAR\RAS\SAT\TAU\UAV\VAW\WAX\XAY\YAZ\ZBB/!BC/"BD/#BE/$BF/%BG/&BH/'BI/(BJ/)BK/*BL/+BM/,BN/-BO/.BP//DS\[DT\\DU\]DV\^DW\_DX\`HS/[HT/\HU/]HV/^HW/_HX/`J1/{J2/|J3/}J4/~LA/aLB/bLC/cLD/dLE/eLF/fLG/gLH/hLI/iLJ/jLK/kLL/lLM/mLN/nLO/oLP/pLQ/qLR/rLS/sLT/tLU/uLV/vLW/wLX/xLY/yLZ/zMR/:MS/;MT/<MU/=MV/>MW/?MX/@NR\:NS\;NT\<NU\=NV\>NW\?NX\@OB\!OC\"OD\#OE\$OF\%OG\&OH\'OI\(OJ\)OK\*OL\+OM\,ON\-OO\.OP\/P0/0P1/1P2/2P3/3P4/4P5/5P6/6P7/7P8/8P9/9PA/APB/BPC/CPD/DPE/EPF/FPG/GPH/HPI/IPJ/JPK/KPL/LPM/MPN/NPO/OPP/PPQ/QPR/RPS/SPT/TPU/UPV/VPW/WPX/XPY/YPZ/ZQ1\{Q2\|Q3\}Q4\~SA\aSB\bSC\cSD\dSE\eSF\fSG\gSH\hSI\iSJ\jSK\kSL\lSM\mSN\nSO\oSP\pSQ\qSR\rSS\sST\tSU\uSV\vSW\wSX\xSY\ySZ\z@AAŁ@ Aρ@ ʁ@  AAہ@ށ@Aԁ@AAс@01A3@26@754A<@=?>A:;A9@8(@)+*A./A-@,$%A'@&"@#! A`aAc@bf@gedAl@monAjkAi@hx@y{zA~A}@|tuAw@vr@sqpAP@QSRAVWAU@T\]A_@^Z@[YXAHIAK@JN@OMLAD@EGFABCAA@@#2$FW6etHZӾl~3"V,Gu>dɜ@ۿRdv!0&gv4DUJüXџn|ك1 w.fT@R+:dN_vm|$ÿ6H ;Z*^lO}~l .ǟ䩐 2ZLKy^hh ?z.ĕ*8FkzTHYb-/ xi2[JLţ^׀hzƃM \n .(<9BPft]L~o& 48)JXn|l}O^*,; >@Rdv|m_N$:+6HZl~"ܹ0N$\5AjPbxs*ݣ8FT4%bQ@prc 2&L7^`hqCzR (:߳D6'V`parSBؙ&4EJTfXwn |1 ك.@teRWFd2#vnetresolve nrcount=%d nr[%d] re_resolve_time in future (%d secs) nr[%d] resolving of %s:%s failed, error: %s nr[%d] resolving of %s:%s success! System time has gone too much forwards, Resetting timer '%s'. dt=%d margin=%d System time has gone too much backwards, Resetting timer '%s'. dt=%d margin=%d =RM , ^Ho؋Ȅ  $Xԑ oĐoo0r•ҕ"2BRbr–Җ"2BRbr—җ"2BRbr˜Ҙ"2BRbr™ҙ"2BRbršҚ"2BRbr›қddcd-1??q`@@`=8CKK8C=8=GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2$,"^X# j L?,`uApRQW+;{8/u@DL+ML\ hlS}0~W A'?/sd0~m 8k#ѳ@V WJ[$X^!u_IO_stdin_usedX gaprx_syslog_initprinttimeaprxlogtimetickUmainfd_nonblockingmodej aprsis_thread| pthr_attrs die_now mycall tocall tocall25 myloc_lat myloc_coslat myloc_lon myloc_latstr0 myloc_lonstrB swnameT swversions time_reset debug verbout erlangout rflogfile aprxlogfile pidfile log_aprsis9j rttyreader_getcttyreader_initaprx_cfmakerawttyreader_registerttyreader_parse_ttyparamsttyreader_parse_nullparamsNttyreader_newttyreader_serialcfg^ttyreader_linewritettyreader_prepollhexdumpfp,ttyreader_postpollaprsis_threadpthr_attrs?,6parse_ax25addriax25_to_tnc2_fmtaddressax25_format_to_tncax25_to_tnc2aprsis_threadpthr_attrsuA aprsis_init aprsis_set_heartbeat_timeout aprsis_set_loginc aprsis_set_filter aprsis_add_server aprsis_configBaprsis_postpollaprsis_prepollaprsis_stopaprsis_start~aprsis_queue+aprsis_thread=pthr_attrsaprsis_loginid}QW#hbeacon_childexitbeacon_postpollbeacon_prepollq!beacon_config.#aprsis_thread@#pthr_attrsC;{cEscan_intvalidate_degmin_inputOconfig_parse_intervalconfigline_is_commentJreadconfiglinevalidate_callsign_input$config_parse_booleanconfig_STRUPPER config_STRLOWERQconfig_SKIPTEXTconfig_SKIPSPACE readconfigtaprsis_threadpthr_attrsaprsis_loginQ netax25_initnetax25_sendtonetax25_postpollnetax25_prepoll4netax25_opennetax25_addrxportCnetax25_startxnetax25_sendax25aprsis_thread pthr_attrs3# erlang_initeerlang_start3erlang_postpollgerlang_prepoll erlang_addy erlang_set! aprsis_thread3 pthr_attrs erlanglogfile erlangsyslog erlanglog1min erlang_backingstore ErlangHead ErlangLines ErlangLinesCount erlang_data_is_nonshared}aprxpolls_reset*aprxpolls_freeRaprxpolls_newaprxpolls_millisaprsis_threadpthr_attrs+!telemetry_configtelemetry_prepoll=telemetry_postpollitelemetry_startaprsis_threadpthr_attrsLw igate_start"rflogtnc2_verify_callsign_formattigate_from_aprsisverblog~igate_to_aprsisaprsis_thread"pthr_attrsX!cellhead_to_clientptrDclientptr_to_cellheadgcellfreecellfreemanyFnew_cellblockcellmallocmanyNcellmalloccellinit8historydb_noposhistorydb_nointerest historydb_hashmatch historydb_keymatch* historydb_dataupdate; historydb_prepollo historydb_lookup historydb_dump_entryb historydb_dump historydb_alloc historydb_free< historydb_postpoll historydb_insert_heard historydb_insert_m historydb_insert historydb_atend historydb_new5historydb_initaprsis_threadpthr_attrslastposition_storetimehistorydb_cellsize%historydb_cellalign9Szkeyhash_initkeyhashkeyhashucU"parse_aprs_messageparse_aprsaprsis_threadpthr_attrs Sdupecheck_getqdupecheck_prepoll$ dupecheck_puta dupecheck_postpoll dupecheck_pbuf dupecheck_aprs dupecheck_new dupecheck_init aprsis_thread pthr_attrs dupecheck_cells duperecord_size duperecord_alignr'"kissencoder.kiss_pollkiss_kisswriteFkiss_pullkissaprsis_threadpthr_attrs?r%interface_is_beaconableinterface_is_telemetrable=find_interface_by_indexointerface_transmit_ax25interface_transmit_beaconinterface_receive_ax25Gfind_interface_by_callsignprocess_message_to_myselfinterface_receive_3rdpartyinterface_initinterface_config$aprsis_thread$pthr_attrs#%aprsis_interface5%top_interfaces_groupG%all_interfaces_countY%all_interfacessd Cpbuf_getapbuf_putpbuf_allocpbuf_newbpbuf_initaprsis_threadpthr_attrspbufcell_sizepbufcell_align~m,digipeater_find_dupecheckdigipeater_prepolldigipeater_postpollLdigipeater_receive'digipeater_configc,aprsis_threadu,pthr_attrs,ratelimitmax,rateincrementmaxFkf filter_lat2rad filter_lon2rad filter_preprocess_dupefilter filter_parse_one_s]filter_postprocess_dupefilterfilter_processFfilter_freefilter_parsevfilter_initaprsis_threadpthr_attrsfilter_cells/hist_lookup_intervalAfilter_cellsizeSfilter_cellalignѳdprsgw_prepolldprsgw_postpolldprslogadprsgw_pulldprsaprsis_threadpthr_attrsdprslogfilelcalc_crc_16check_crc_16calc_crc_ccittxcalc_crc_flexaprsis_threadpthr_attrs&crc16_table=crc_flex_tableTcrc_ccitt_table/4kFaprsis_threadXpthr_attrs}netresolv_stopvnetresolv_startnetresolv_addZaprsis_threadlpthr_attrsnetresolv_threadJeFtv_timerdelta_millistv_timeradd_millistv_timeradd_seconds5timecmpbtv_timercmptv_timerboundsaprsis_thread!pthr_attrs3nowEtick DD\ointy^OK'/build/buildd-glibc_2.7-18lenny7-i386-lqGp8g/glibc-2.7/build-tree/i386-libc/csu/crti.S/build/buildd-glibc_2.7-18lenny7-i386-lqGp8g/glibc-2.7/build-tree/glibc-2.7/csuGNU AS 2.18.0 ]0\oint8ao1- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#lh+    # # E,Z#  ' zC a{# <|# Fl aG# `H# 2E$ 6  7 8 #~ 9wZ )fd *Z# $ +S# # ,S#tm, Z# .Z# Z# Z# fZ# Z# hZ# QZ# Z# 8#$ Q#(oimO  17V u Z #  Z#  Z#  C#  3Q_ # uZ#[ U',ZyiZLZ;`buf aZ!tvCl!t@YDqfmt;"#apEFq$ fp\ ;%1B Z+&sigAZ'CZp(pidDZW%6`u)sig5Z*+buf; ?,(r9L+tszl-iZ.K/Z@N0lZ0(iZC'uH'{uL-pZ-y=Z-Z+appP1_`1L828?pf0$Xf;Z3Ipf O*Irc Z#er Zpid Z*I~pid#Z,a()fd`ZT(__ibZ' lC'6oZ 3 Q1 @f 44445 ;5 ,l5 - 4! 5=.Z 51H5s%%5&  f#55 5B!D5D"5q#515E24  C5+kZ5oZ5<Z5Z55E5,6 Z5/Z8 \0\oint8ao 01- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    ' M!LoS 2E$ 6[  7[ 8 k #~ 9<   )fd *Z# $ +S# # ,S#  F a G# ` H# 7 0 0f <  O # O !#  "# ## $# ( %# e &#4 2 '#8   ,E X \#  _E# bE# e#  j# P p# G s%# y0# 0# 0# 0# 0# 0# 0#7&  17n2>40 %' +V   Y>D # D# T ! A h / M u  )+ , ../< 23- : ; <Zx 4x \   Z# Z# Z# Z# ' # !# k"# ##- .#db/( # 1# ^ 2# 3. # 44 # 6D #$ g/9#( I:#* ;#, <#.key=K #/lat?D #< ?D #@lon?D #D @#H B#L C[ #PP +( (Hk # K# L# M# N# O# ;P# D  [ k { P Q. l*_ G_ # I&# J&# K&# U M&# O&# P&#tR# n S# g/T# IU# V# W# X# Y#" [Z#$ E\Z#( "^#, _#0 `#4 < a#8 Z b#< c#@ d#DlatfD #HlnggD #L hD #P ;je #T Jlu #X mZ#\ tou #` e pZ#d .r#h u k\ #fp # Z# Z# buf # IVu* Z* # Z# Z# # c   ? v0  q } n  !,fdZ# # # &Z# Z# c #  # N ,# Z#@tio #D # /<# 'L#  \# l# T% |#  #  Z#  Z# R# # xZ#" .#" WZ#B hZ#B #B <  L  \  l  Z|    < # +Z# &# &# /# FM# Z# 3 # 0# S #! 0# ;0# 0 # z0 # r Z#$  #( m#,tty#0 Z#4 #8    j  # (#t#  # 4- _ # E &# =&# : &# U &# &# :&# v# #4 #8  D6 ' Z# !6# F " It  \NR3S TZ# JUZ# VZ# XZ# Y # H Z#Z l]s k ^5# _t# x`# AaA# L bG# cG# e#  f# 5 gM# hM#' kD #0 9 lD #4 mD #8 rZ#< XsZ#@ 3tZ#D u]#H G wZ#L ]xc#P zZ#T {c#X }Z#\  ~c#` Z#d c#h0Q5 4# D # 9 D # D # 5 D # D # o# }u# P {# {#$ Z#(  #,s ; ] iF{ 3 - K "!! WZ!"MZ8#SL$bZg#Sa%pcg&dZm'd(.)W pu*.ڪ+t@+fZ_ *,tty-g Z %,cf+tty+str.i!Z_/"0!#ZuX1 $0I %Zu\2p63451 1Z6 1$ %1q%6,1$ %1q%66CR1$ %1q%6GTx1$ %1q%6Xe1$ %1q%6~1$ %1q%61$ %1q%6®1$ %1q%6ʮ׮61$ %1q%6ۮ\1$ %1q%6Zg1$ %1q%2361$ %1q%236;H1$ %1q%23661$ %1q%6>K\1$ %1q%6dq1$ %1q%7u0 Zup{ 7-+Z0N+cf@+ttyl+str1 0I Zup2+371$ %1q%-j6 8tty1Z- pKb ^,cf9  +str .tty 4 1$ %1q%6̴37̴1 1Z588lenZ1`1:SP71 ;S] Sv8ixZ?8par1`11 8req8ai8iZ-fZ@4 +app ( @idxZu@iZu@Su.pfd* ` 4P/' [Z~ /b ]Z Ah?35B 5(Cu@B B" DCHCupB w  : !@ ;fp ;buf g ;len # E  ZC S8iZ1Z$Z'#S%cZ& 'F3?3I?DRD[-wxZ\ %+appw  @idxyZud.iyZ .S{=@P|* uhA35B[BGI_37IBDH883I5PDRB[Ih3J 6J ZJZJ^ KKL,1L-k K K+ZKoZ2-`f 0\oint8ao- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'M!LoS FD aG# `H# 2E$ 6n  7n 8 ~ #~ 9O   7 0 0f < > O # O !#  "# ## $# ( %># e &#4 2 '#8 N  ,E XN \N#  _E# bE# eN#  j# P pN# G s%# y0# 0# 0# 0# 0# 0# 0#7& Y  17n 2> 40 -  .#db /# 1# ^ 2#  3#  4#  6.#$ g/ 9k#( I :k#* ;k#,  <`#.key =5#/lat ?.#<  ?.#@lon ?.#D  @v#H  B#L  CE#PP  + ( HU#  K#  L# M#  N# O# ; P# .  E U e P Q. l *I GI # I# J# K# U M# O# P#tR# n Sv# g/Tk# IUk# Vk# Wk# Xk# Yk#" [Z#$ E\Z#( "^#, _#0 `#4 < a#8 Z b#< c#@ d#Dlatf.#Hlngg.#L h.#P ;jO #T Jl_ #X mZ#\ to_ #` e pZ#d .r#hp _ `Ԙ   ? ve  q }   !a fdZ# # # &Z# Z#  #  # N a # Z#@tio#D # /q # ' #   #  # T%  #   #  Z#  Z# R#  # xZ#" . #" WZ#B hZ#B #B q       Z    < # +Z# # # /# Ff# Z# 3# 0# S `#! 0# ;0# 0 # z0 # r Z#$  #( m#,tty#0 Z#4 #8 `  `. j    # (v#t#  # 4- I # E # =# : # U # # :# # #4 #8. # DO ' Z# !O# _ "#I  \Nk3S TZ# JUZ# VZ# XZ# Y# H Z#Z l] k ^N# _# x` # AaZ# L b`# c`# e#  f# 5 gf# hf#' k.#0 9 l.#4 m.#8 rZ#< XsZ#@ 3tZ#D uv#H G wZ#L ]x|#P zZ#T {|#X }Z#\  ~|#` Z#d |#h0QN 4 # .# 9 .# .# 5 .# .# # }# P # #$ Z#(  #, T `v  T_e3 - K DKZ`Ai 40J_ m J SJZ!iLZ1"MZO!cNZ #srcR }Z!iZ!cZ"Zj"9Z`Z_ % 0 uv$]  $  !iZ&!jZs!s!e!t"6Z-%&G&cZZ'h Z`fY(aif ) )! )  *% *0 + Zup+ui,Z+Zul+ Zuh+Zud  --.,D.-~ -oZ pC 0\oint8aoZ  01 - HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l    # # E,Z#   ' MB VB#ER + &R  F a G# ` H# 2E$ 6  7 8#~ 9  ) fd *Z# $ +S# # ,S# ,mO' +dV   Y>  d# # ()8w G!o I !A h / M u  )+ , ../< 23- : ; <Zx 4x \  f Z# Z# Z# Z# '  # !f# k"# #l# o k\ #fp# Z# Z# buf# I Vu8 Z8# Z# Z# h#  +c Y4 5# 6# (7# 8# M9# :Z# ;>#PL> J?Z#H@# A # B # CZ# =DZ# CEZ# FZ# eGZ# .I#$ J#} K#ܔ c>  > # Z# \Z# Z# &#WZ; 9Zn An iZZ aZ app` pfdb8Acn  ,ap! TZ% " S #iUZQHVZ!rZdc "(q#isZ#Ht!cZ4 "$b#idZ#He!6Z@6 $54$5`H7!JZ@Q %cfI K& K#strL.'I MZuP'NZuTAIHP( $ f%qf%))6 $ s%qs%)(5 $ %q%)`m $ %q%)3 %(a 'ZP* +. )% $ %q%( ,iZup) %)$ $ %q%( l1Z,l2ZuX)hu3 $ %q%([ -  Z) $ %q%)m .m  Z.$ %q%   7/hZ`b1%fdZ#iZ,buf1ܱB'!X,ZD%app+ #i-Z,#pfd.8U!ZP|h0app idxZ#pfd812$'#iZ'p31v0(\4Aun T4whyu}5Z4An 6 <6&E666Z7iZ`8.x9Z7lenZ7p.:4An ,reqH#ail(#al,ap~#iZ/ #nZy s'*g& &TZ !; il:?+ ?@ @ A\q}Z3app iZpfd8An ?+M ?@W @a ZdAn iZcZ:lI!~'\]~B(-@CX-xD"@E&v)D+-D%"D z"D"B; +M -@W Da "B3+E-@DO"DY#Ft B~ + -D V#D i#!jZP#r" <$Z#$&#$5$"$ZU$#i"Z$#len"Z$\"Z#p#$'&$XC%Z,buf 'a!Z r8{2y8On 8P  8QZ$8|RZ(8bSZ8VZGGGHH G!G=^ZG1GGEG hG+ZGoZHNLG]Z#w |)0\oint8aoZ 1 - HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l    # # E,Z#    ' M!LoS  F_ a G# ` H# 2E$ 6  7 8#~ 9j  )fd *Z# $ +S# # ,S#  7 0 0 f <  O # O !#  "# ## $# ( %# e & #4 2 ' #8 ,E X \#  _E# bE# e#  j# P p# G s%# y0# 0# 0# 0# 0# 0# 0# 7&  17n2>40  - .#db/s# 1# ^ 2# 3y# 4# 6#$ g/9#( I:#* ;#, <#.key=#/lat?#< ?#@lon?#D @#H B#L C#P P +s (H# K# L# M# N# O# ;P#   P Q . l* G # I+# J+# K+# U M+# O+# P+#tR# n S# g/T# IU# V# W# X# Y#" [Z#$ E\Z#( "^ #, _ #0 ` #4 < a #8 Z b #< c #@ d #Dlatf#Hlngg#L h#P ;j #T Jl #X mZ#\ to #` e pZ#d .r#h   k\  #fp # Z# Z# buf # 0 I Vuu Zu # Z# Z# 6# Ԯ   ? v{  q }   !w fdZ# 6# # &Z# Z#  #  # N w # Z#@tio"#D  # / # ' #   #  # T%  #  "#  Z#  Z# R# "# xZ#" .3#" WZ#B hZ#B #B     Z   < # +Z# +# +# /# F|# Z# 3# 0 # S #! 0# ;0# 0 # z0 # r Z#$  #( m#,tty#0 Z#4 #8  3Dj # ## (#t#  # 4-  # E +# =+# : +# U +# +# :+# # #4 )#8 D9De ' Z# !e##u"9I  \N3S TZ# JUZ# VZ# XZ# Y# H Z# Z l] k ^d# _# x`# Aap# L bv# cv# e#  f# 5 g|# h|#' k#0 9 l#4 m#8 rZ#< XsZ#@ 3tZ#D u#H G wZ#L ]x#P zZ#T {#X }Z#\  ~#` Z#d #h0Qd 4# # 9 # # 5 # # # }# P # #$ Z#(  #,   j  #   u  3   - K  ,  # LZ# T%#src #  #via #msg # V #  #  #$  #% +Z#(  4 ;# 6#  # "Z# #Z# %Z# &Z# M'# (#$ )Z#( *Z#, a+#0   7 V !buf aZ"fp #"p$u &t"bmv %WZ*  &5C'&4$Ph &P"iQZ(o$)pidZ$*iZ(%+,&;%+-buf?._N%N)txtz%/Z%0Z"minZ*secZ&-hmsd7Z V6  +6Z &6"p8"pid9Z0:Z.-&/&Y&)bmx&0Z0Z,4Z&*iZ&*txt D'*msg'1s222+83(456h ,/ D(*src ("lenZ0E6"dp40$ %0q%+0$ %0q%6*aif),/ _)*src )"lenZ0E6@"dpU47I0$ X%0qX%40$ 3%0q3%7Nh2x2l2`+8d53%*.io*r)arg*,&r*9^*iZ**t*+"rZ,LZ+ x$ &"rcZ0)Z:"p#"bm#"p#"bm!;Z%&+)appR+-idxZuh-iZul-Pu up+,&+12+@3+3+65+3>,+3,+( 3,7P 2+h 3- 0 ;{vZ0$-U)appuP-*iwZ|-+ ,&}-4*pfdu -<?-@!=cf:@!-=p1;  .=str<,.>=F!0>&>02?@ \2?+A 2@viaB k3?C 3AaDZBbufE@toF 3?yG3@latH 4@lonI r5?ZJ5?0 K16@aifL6?I MZ(7BbmOC=DA$ \%Aq\%6 + A ]K!A]ZDA$ %Aq%6 )+ A K!AZDMA$ %Aq%DWs"len2Z"msg3E*:27D>KA$ %Aq%DSv4SvA K!AZDWdA$ %Aq%D'A$ %Aq%DyKA$ %Aq%D$1oA$ %Aq%D0$ %0q%D40 K!0ZD40 K!0ZD $ %4 $ 0 K!0ZD  U4  0 K!0ZD  4  0 K!0ZD~  4~  0 K!0ZD  4  0 K!0ZD   4  0 3K!03ZD  ; 0$ G%0qG%DU b a 0$ Z%0qZ%D  4 0 iK!0iZD 0$ {%0q{%D<I 0$ %0q%D!0$ %0q%E .!24Zg0$ %0q%  Z Q!7$Eq! E;)ZZ |T7")cfY@!70[0 [*str\79]ZuT9I ^ZuX0&a6 "0$ t%0qt%D8"0$ w%0qw%D PS"-vxZupECk"2Q4k3]Y8FV!T2d!D"0$ %0q%4@M0$ %0q%G[.", G/Z0HHHI,_I- H! H1 Hs% HD Hq Jnow6H 6H+ZHoZKZKG# _cv/0\oint8ao1-t HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#{  t  '2E$6 78 ) #~9 R k\ #fp# Z# Z# buf# I  ZF~Z }}bZvalZcZWZE+l8p*8len*Z8val*89*09i,ZQc-R>Zr9Os=9o=Z:deg?Z!:m1@ZM:m2@Z`cAy:tB:9CZud?ZO;par{;m'retZ;  !!"8 #$;$f<LZy<?cf?<bufaEc<iZ=RZD=cf?p=%=%aZ >"` p)>&&Zs>$'/%>'8%Z>(i'Z(p()c)0?*5*ZY?++ZupxZ?par?,- Z-x}-x} Z7.h0? /sÜ@)cZ;@0U.0N@Q/sz@)cZ@0P"&(p@/Yo@'0ohA)OqA*crA)lensZA1 2iZP2hxBi0K2&B# /Y2B0FB+Z\ 3cf*?, ,str-4 $ ;%q;%54 $ ?%q?%54 $ B%qB%54 $ E%qE%54 $ H%qH%54 $ K%qK%54= $ P%qP%54\ $ h%qh%54  jklatl;lngl;imZlamZlomZlacnlocnhp\ 4 6 rrZ66 Z41 $ %q%54P $ %q%54o $ %q%54 iZ6!84 $ %q%54 $ %q%54 $ %q%54 $  %q %54! $ %q%54@ $ %q%56$ %q%57Z 8cf?9ќ9 ќ(strҜ9I Z4 9$ %9q%54 9$ %9q%54 9$ %9q%54 9$ %9q%549 9$ %9q%54X $ %q%54w $  %q %54 $ %q%56$ %q%5Z/tB6BcfR@%I ZBiZC:# (!5 " #@ #L $X C10 _$t `E %H !81` #a #m $y E$ E$ E$ F$ (F$ HF$ sF# 1 ;;" # # "8 ;;"P # # a h L!r " #| # $ F# -!!eZG F 6<<=,=-) =!P<1<;<;UZ>M /5D0\oint8ao\ 01- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'M!LoS Fe aG# `H# 2E$ 6  7 8  #~ 9p   )fd *Z# $ +S# # ,S#  7 0 0f <  O # O !#  "# ## $# ( %# e & #4 2 ' #8   ,E X \#  _E# bE# e#  j# P p# G s%# y0# 0# 0# 0# 0# 0# 0#7&  17n2>40- .# p/%#' +DV   Y>x D# x#  ; # 9# # %# # B%# EZ# 7  - .#db/# 1# ^ 2# 3# 4# 6#$ g/9#( I:#* ;#, <#.key=#/lat?#< ?#@lon?#D @#H B#L C#PP + (H# K# L# M# N# O# ;P#!      P Q!. l* G # I1# J1# K1# U M1# O1# P1#tR# n S# g/T# IU# V# W# X# Y#" [Z#$ E\Z#( "^#, _#0 `#4 < a#8 Z b#< c#@ d#Dlatf#Hlngg#L h#P ;j #T Jl #X mZ#\ to #` e pZ#d .r#h Vu5 Z5 # Z# Z# <# n   ? v;  q } y  !7 fdZ# <# # &Z# Z# n #  # N 7 # Z#@tio##D # /G # 'W #  g # w # T%  #  #  Z#  Z# R# # xZ#" .#" WZ#B hZ#B #B G  W  g  w  Z    < # +Z# 1# 1# /# Fh# Z# 3 # 0&# S #! 0# ;0# 0 # z0 # r Z#$  #( m#,tty#0 Z#4 #8      0 j  # (#t#  # 4-  # E 1# =1# : 1# U 1# 1# :1# # #4 #80 % DQ ' Z# !Q# a "%I  \Nm3S TZ# JUZ# VZ# XZ# Y # H Z#Z l] k ^P# _# x`# Aa\# L bb# cb# e#  f# 5 gh# hh#' k#0 9 l#4 m#8 rZ#< XsZ#@ 3tZ#D ux#H G wZ#L ]x~#P zZ#T {~#X }Z#\  ~~#` Z#d ~#h0QP 4# # 9 # # 5 # # # }# P # #$ Z#(  #, V x a3  - K ,mD1c 'm  x?q rE# sE# t>#irqu7# dmav7# w7#  oOLOOO@OSZZm  O  # #  b># ># Z# ># 7# 7# y# G H# . JMp ND# O.# KPZ# u05 6Z# 71# J8h# 9# ;# < # /=# T%>#,    R^fdSZ# TZ# /U# T%V# JW9#!"Z"/!#i#Z$%$ %%%q%%&r'arg#tv<&N"/""Z(//G)a/1G*mH* oH*H*H*wH%>+sllT+c0k,+mh-iZ3I#lenZ   .!,Z17QID-fp- }I+ifr.oL, /D{-s/I+fd0Zu{,1p-d1UJ-i2Z/J/^@0p1X2|J3444455A6-jZJ366-jZJ-dU K366%$ %%q% Up.6Z7s:K6fd5HK+sll7T,8up7n9Z[K79ZyK-i9ZK7U:UK,;g1x-fp K1,0 0 @ ;8 ::Ly6fdBL+bufyp 9Z:;UL6appL-iZL-jZL-pfd5 L:D 9Z;<Lm6app'M-pfd5 ]M-iZM;`<<03`<<4rcsZ?tZ>tiou#?v?Fw>ifrxo>fdyZ?>z.?{Z?Y{Z@4$000  ? . ? 9<@M*MAm 0~12M2|NBB~B~B@2N2N2$O2POC@DX0009)@A|OC*/O*T%O%m.)2ABOx-iZ*P%ZEB5DHPFmtPF40PFZPGrcZPGpZlQHfo?> 3  HB+4UHQCZ8H'[Z H\ZH"^u<.Hh_Z@Ha DH.bHHcZL, <PIIIJ,eJ- I <I+ZIoZIE wL!@DL"0\oint8ao!Z1- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    ' !dM FO aG# `H# 2E$ 6y  7y 8  #~ 9Z   )fd *Z# $ +S# # ,S#  17V uE Z E#  Z#  Z#  &#  g   ! K!  ,! # ! # ! # #  # ! # #!  |! # K1 Z#  #  #' #( " Z#, ! s#0 ! s#L " Z#h  Z#le10 #p   s L C  # G Z# Z#$ #( !! #, #0 1 C#4 ! #D S u ;} &<# =}#L   ZZ$"A p@DDQ oQ]"DDQ ' V OZDEQ_!NZ!R!A NZJR"EFP_sR"iQZRSA"EGFRZR#p$%l!PFHR&iZu{'!{'D&fpu{()EB-S -*+!ZIJI#Sa,appa+N"ZPIIOS-appa{S.!2-IJS 0S1ZS)i3ZT)E4-.T/'J?J0$ @%0q@%K JKbTy T-erlgTo ZZ )E-TLLU Z17!&X1 1 &`1U!1!4h223 ,O3 - 21 2  &2+ Z2o Z2 Z3,l3)Z\3U*ZT3-3 6 X37 `-30 8Zh3!9Zd9z"LL$0\oint8ao- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    ' F aG# `H#2E$6B 7B8 R #~9#)fd*Z# $+S# #,S#  V u Z #  Z#  Z#  # ]"LLDU$app$"0 L@LpURapp0$"@LLUapp$UpU7i"ZLLVapp$ , -R   5$M\%0\oint8ao1- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'M!LoS FO aG# `H# 2E$ 6y  7y 8  #~ 9Z   )fd *Z# $ +S# # ,S# 7 0 0f <  O # O !#  "# ## $# ( %# e &#4 2 '#8   ,E X \#  _E# bE# e#  j# P p# G s%# y0# 0# 0# 0# 0# 0# 0#7&   17n 2> 40- .#db/S# 1# ^ 2# 3Y# 4_# 6o#$ g/9#( I:#* ;#, <#.key=v#/lat?o#< ?o#@lon?o#D @#H B#L C#PP +S (H# K# L# M# N# O# ;P# o     P Q. l* G # I# J# K# U M# O# P#tR# n S# g/T# IU# V# W# X# Y#" [Z#$ E\Z#( "^#, _#0 `#4 < a#8 Z b#< c#@ d#Dlatfo#Hlnggo#L ho#P ;j #T Jl #X mZ#\ to #` e pZ#d .r#h k\ #fp # Z# Z# buf #  IVuU ZU # Z# Z# &# Ԏ   ? v[  q }   !W fdZ# &# # &Z# Z#  #  # N W # Z#@tio#D # /g # 'w #   #  # T%  #  #  Z#  Z# R# # xZ#" .#" WZ#B hZ#B #B g  w     Z   < ## +Z# # # /# F# Z# 3# 0# S #! 0# ;0# 0 # z0 # r Z#$  #( m#,tty/#0 Z#4 #8  $! ,!# ! # !# # # !# #!N |!# K1Z# N# #' #( "Z#, !$#0 !$#L "Z#h Z#le10^#p ^  $n j M M# (#t#  # 4-  # E # =# : # U # # :# # #4 S#8n c D ' Z# !# M "cI  \N3SA TZ# JUZ# VZ# XZ# Y# H ZA#Z l] k ^# _# x` # Aa# L b# c# e#  f# 5 g# h#' ko#0 9 lo#4 mo#8 rZ#< XsZ#@ 3tZ#D u#H G wZ#L ]x#P zZ#T {#X }Z#\  ~#` Z#d #h0Q 4 # o# 9 o# o# 5 o# o# # }# P # #$ Z#(  #,   M3G# - K A$ z 4! #  "z# ##Z# 9 $#  ". arg-!tv/&#4 arg3!tv5"`#ZMQHVw#cfwtV$% V&strV'I Zu`(aif ud' zuh'#Zul'9 up)$$ %$q%*MM$$ %$q%*MM$$ %$q%)+$ }$Z*MM,$$  %$q %)F%# W)0`%4W,OIP$Z#1 75-$LZQmRW.appKX/0RLRO0,0RLR12LRmRP0,LRmR1 3$pRS;X4+#gX4H#X#bufX5a&iZX%#Z Y%-Y+P%#MY Z6#wSXkY7ixZY7jxZY7kxZZ7txZXZ8bufyS}7sy1[9azZ[:H#{;"|Z9S#}\;"~o!fo+h7E.\9+# b\  ;3"NY[[\=&iOZ\(bufPS}&sP\%aQZ\'H#R$"SZ+#;`\\V]/|\\E0,|\\12\\F0,\\1?5#?q# :^$Zl@#:#&p:"&x:'$Z:M$Z:#'Z:A$(lAAB,OB- A!A1As%A &A+ZAoZC C0 Zs% ^%\h)0\oint8ao1- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'M!LoS FO aG# `H# 2E$ 6y  7y 8  #~ 9Z   7 0 0f < I O # O !#  "# ## $# ( %I# e &#4 2 '#8 Y  ,E XY \Y#  _E# bE# eY#  j# P pY# G s%# y0# 0# 0# 0# 0# 0# 0#7& d  17n 2> 40 -  .#db /# 1# ^ 2#  3##  4)#  69#$ g/ 9v#( I :v#* ;v#,  K0Zf?&Zup@{$:dAK0^B˥939(9,:<BF9k9`9W,(:tCp999,:DDE,OE- D &DoZD<ZDFM% T#h&hl*,0\oint8ao-i HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#p  i  '&& ''Z# &(Z# = )Z# &*Z# *&+Z# 2&-# &1# I&2# S&4Z# &5Z#$ &8Z#( ]&:#,'$ D#  '&vΚchӿ'!chſpǜhhfD hhgg&"h=i2gca!^gp!}gch#<&E@iig@caDg 'D@g&DZ4hE FZQchJRniqiJYniqiu&NZickjh caMh!iOZh!cbPh!fdUZh"Vo#!rc`Zi!ch}9i $&ZpkkWiN cai% '@i%&Zi&E Zj!chZj&kLlxj ca٪jcpۚ!chܿj';l=l6;l=l&Pllk8%2&;k%'8[k% &8{k%&8k%&8k%*&8kcaZ((4%(l}-0\oint8ao1- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'MoS FD aG# `H# 2E$ 6n  7n 8 ~ #~ 9O )fd *Z# $ +S# # ,S#  17n 2> 40& & -  .#db /|# 1# ^ 2#  3#  4#  6#$ g/ 9#( I :#* ;#,  <#.key =#/lat ?#<  ?#@lon ?#D  @#H  B#L  C#PP  +| ( H#  K#  L# M#  N# O# ; P#      E  P Q. l * G# I# J# K# U M# O# P#tR# n S# g/T# IU# V# W# X# Y#" [Z#$ E\Z#( "^#, _#0 `#4 < a#8 Z b#< c#@ d#Dlatf#Hlngg#L h#P ;j#T Jl#X mZ#\ to#` e pZ#d .r#h  Vu% Z%# Z# Z# # (ZWh1Wh200C(9db8+hp:cp:i;Z(;Z(?]'.)('(llkll%lmmQlmm}l m%mlU(\Z0m7mli app[i (  @mtnm db +-m 4' Mm   mm!iZm"h10ul!cpm#@'up$1@%B&X'LZ(x(rn^onS )fpq)hpqS Y *(%'~`oo-n +db} Yn)fp},iZyn,hpn-(n . (Qo%pn +dbP+n/PZ,retRo('D0pp2o< +pC^o(aZpqo app`i "ibZul$\pl%j&0uo0o1up'0o'h qup dbg+;ppbg p!iiZp!h1j0pkZ!hpl2q!cplqq!cp1lq#(nu`#4'pasq$1%B&'L *. .( u6|rm +db+,r+pb r2' riZ,h10vs-')Zs3Z,hp:t,cprt,cp1t4(u\44'a,st.g( @|b|t )db+)pb ( )ap||(u 5jbZup,ibZTu6||,dbd+gu,hpezu,hp2eu.)6+|>}u5db7+7'%@}}u4;'[+4B)Z4M'#r'Y889,D9-~ 8 8oZ:.(Z9'Z9'! L9(" Pv(_)}%~1oint 1L\4ei)(}}vM)+Z}}:vp*fvlen*3v (*Zv u,R i-3   AU)DZ}%~vpCwlenC33w (CZ^w uE iF3Q cRZR)b*0~j20\oint8ao-t HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#{  t  'MoS2E$6/ 7/8 ? #~9 Z  1++))+ )3,v)H*+Y*)  17n 2> 40 -  .#db /b# 1# ^ 2#  3h#  4n#  6~#$ g/ 9#( I :#* ;#,  <#.key =#/lat ?~#<  ?~#@lon ?~#D  @#H  B#L  C#PP  +b ( H#  K#  L# M#  N# O# ; P# ~     E  P Q. l *  G# I#  J# K# U M# O# P#t R# n S# g/ T# I U#  V#  W# X#  Y#"  [Z#$ E \Z#( " ^#,  _#0  `#4 < a#8 Z b#<  c#@  d#Dlat f~#Hlng g~#L h~#P ; j#T J l#X mZ#\ t o#` e pZ#d . r#h  + . ; # |+ # S+ Z# + Z# * Z# , Z#,)ZIc(-*#Zdc")(Zpb';'+'\+eZ0~Jwc1dwc2dLx+dQ1+d*WZPy9 pbV9 yamVH y pXy? !. M <,5Z{z pb4>zlat4 rzlng4 z"+4z"1+4{#bad6ZQ{~$) Z{ pb {%; {%+ |&+ N|&1+ y| iZ|'b8Z':Z'*Z')Z'*Z')Z'*Z'*Z(lat~(lng~$*HZ2| pbG|%;G }%+Gj})o*IJ)Q*J0ul&)J0}&+J0})+J0u`&+J0 ~&+J09~*latK~_*lngK~]&J+L~&(+L~&+M~&1+M&*NZ.&)OZW+.dt,?)9Z pb8;8 +8 (lat:~(lng:~'Q*;0');0'+;0'+;0'+;0'+;0'+,<'*= (p>'+?'1+?'+@Z(iAZ-,? 7  :+1Z pb0;0+0(lat2~(lng2~' 3'+3(i4Z(la4Z(lo4Z(lac5(loc5'+6'1+6v*Z5pb.+.1+/+,/0 /)/)Z/+Z*Zpb;+(iZ'=Z'+-,Z+Zpb;+(iZ-,Z*-Z@pb,%},&g/.&%*.&*/Z~ rc0ZW&;1d&+2'*3&Z 4&*6Z0I?g=,Z1 rcFZZ2 s@ peA3؊(cCZ0IQy/,Z4 t,,,5`6C+I,Z4 *,7 ,+ , 57C ]7O [6[ 6g 6s 6 26 6 ɋ6 7 E6 8 8 6 6 +.0L,?4 ,? ,3 ,( 5x6K B6W ΍6c Z6o 6{ "6 a6 6 ѐ6 6 Y6 9 , , , 586 8888)0d9R,,,v45pi,^,R,G58j6t6X+I6N,Z27(p iZ&f;3 rc?ZȒ:::; ,; -? :o Z ,,Ѧ80\oint8ao- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'MoS F9 aG# `H# 2E$ 6c  7c 8 s #~ 9D   )fd *Z# $ +S# # ,S#  17n 2> 40& &. l * G# I# J# K# U M# O# P#tR# n S# g/T# IU# V# W# X# Y#" [Z#$ E\Z#( "^#, _#0 `#4 < a#8 Z b#< c#@ d#Dlatf#Hlngg#L h#P ;j#T Jl#X mZ#\ to#` e pZ#d .r#h  Vu> Z># Z# Z# # j # ## (#t#  # 4- # E # =# : # U # # :# ~# #4 )#8D 9 j DDq ' Z# !q# # "E,argtv-~dp}9J,mdpl-GdpdppG(ZiZdZdpcME q!"-Z#appI$ʞ%ʞ&'f-M t$ (LZ(y-LZ)dpN*lqf+h,l,a -dp2*+" -Z̠t .app/011&1'ܔ20up01:"*-Р #dpcD#pb|3  iZ4(5idx/5dppGM5dp6 < ud4!Z7-#6.$uh7v$%Z4'Z4/(Z*p)05cBZ8,У -dpc( <W( (.(v$ siZ9Z9/Z9(Q)idx)dppG)dpښ0()c=8,>  :'> dp?;W-/Ѧ<,Z<,Z<9- U 6z,==>,9>-s = =+Z=oZ>\,$ p>,' 8>F-( <00-^;0\oint8ao1- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'M!LoS FO aG# `H# 2E$ 6y  7y 8  #~ 9Z   7 0 0f < I O # O !#  "# ## $# ( %I# e &#4 2 '#8 Y  ,E XY \Y#  _E# bE# eY#  j# P pY# G s%# y0# 0# 0# 0# 0# 0# 0#7& d  17n 2> 40 -  .#db /# 1# ^ 2#  3##  4)#  69#$ g/ 9v#( I :v#* ;v#,  ,O>- = &=oZ=E v ?-1?..2n%^3.!/?0\oint8ao1- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'M!LoS  FO a G# ` H# 2E$ 6y  7y 8  #~ 9Z   7 0 0f < I O # O !#  "# ## $# ( %I# e &#4 2 '#8 Y  ,E XY \Y#  _E# bE# eY#  j# P pY# G s%# y0# 0# 0# 0# 0# 0# 0#7& d  17n 2> 40- .#db/# 1# ^ 2# 3## 4)# 69#$ g/9v#( I:v#* ;v#,  q } #  ! fdZ# &# # &Z# Z#  # > # N  # Z#@tio#D # / # ' #   # ! # T% 1 #   #  Z#  Z# R#  # xZ#" . #" WZ#B hZ#B #B       !  Z1  A  < # +Z# # # /# F# Z# 3# 0# S k#! 0# ;0# 0 # z0 # r Z#$  #( m#,tty#0 Z#4 O#8A k  k     j  # (#t#  # 4- _ # E # =# : # U # # :# # #4 #8  D ' Z# !#   "I9  \N3S TZ# JUZ# VZ# XZ# Y# H Z#Z l]8 k ^# _9# x` # Aa# L b # c # e#  f# 5 g# h#' k9#0 9 l9#4 m9#8 rZ#< XsZ#@ 3tZ#D u"#H G wZ#L ]x(#P zZ#T {(#X }Z#\  ~(#` Z#d (#h0Q 4 # 9# 9 9# 9# 5 9# 9# 4# }:# P @# @#$ Z#(  O#,8 E k" ._ {F3EU - K[I +  ;# |+# S+Z# +Z# *Z# ,Z#j0yT }x:!pbx_ "ez"p{#90|Z#1}T`$.jZ%pbi&i&/i'aifk ()$ t%)qt%*)$ x%)qx%.  A +_.&|.%aif%pb%am&*)E)Z'txt)Z't's'iZ+  1,"&!-/QZθ.aifP -/bZи8=/aifb-91 dj0K1jZ10Z 2aifYۥ3 Yu 4Yj3Y 4wYj15.[ZQ)S0\u 6Ae5/_o70Z2aif2src462via4.4/j8J5 ZF)0Z)s.Z8.~9a5 45.Z):)S0Z;881/ `5. Zr9s 9p ک9ve :;`9lenZ k Y    1"0 <2aif3/4 j04jP4S0<4j4.jݫ4u4j4j=9iZ]5 Z};5L/U5}:9pb _ ݬ;9rcZ5|.:5D0&ZM< `@=!=;>+>4?=>HڭBk-r/ q@/AiZ$7~0Z74|.c2pbBamD9rcZ'aif8 \CZ(==w=l;>D0=9=.=#== ;HEF?RE^~?j>v$>n>11˯=2aif4$/4o%g4/4.74/jo9dZ8.=i8C/NT8.u5`/v).Z)W/Z9rcZ').Z).Z9au f9t9pb_ R:p5|.-v;5/EU5.FԳ5.G 5}I:'5|.Kg50LZ5D0LZ8uM=8fNN5 OZP)OZ)PZ)PZ:9vK:5k.;T: 80_b51T̵9iZ :%9v);(5|.[< N k_ o F)1zZ.aif G/H0Z!cfѸ /ц Ѳ!strѲ :/Z"aifӆ #/Z#ֲ# Z# ٲ#r Z#/۲#0Z# Z#;Z#Z#Z#3# Z"p"cZI /()$ %)q%J()$ %)q%J()$ 1%)q1%J($)$ >%)q>%J(C)$ F%)qF%J(b)$ N%)qN%J()$ V%)qV%J('kW()$ `%)q`%J*={ 7Y0Z!۶Q$2cf'aif )5 '9strɷ8 Zup50Z8:/Zu80Zu5 Z?:@ )$ %)q%K )$ %)q%L`!=Ma===;?EuD? EulE u@E+uE6uEAuELuhEWudEbu`>m~Exu>>>NL(b!=;@>YK!)$ %)q%:p!9iZϽK")$ %%)q%%K(")$ K%)qK%KN")$ }%)q}%K t")$ %)q%:"5/ZKkw")$ %)q%K")$ %)q%:"8/Zul: #8/ Zul::#'lenLZ)`M)MK`#)$ %)q%K#)$ )%)q)%K#)$ H%)qH%K#5r KZ.) LK#)$ R%)qR%K$9kSLK>$)$ `%)q`%Db= a$ O/Q$d80\PPQ,OQ- P!P1Ps%P &PoZPRX%QM%AA  Q.(=Z|Q#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'MoS2E$6: 7:8 J #~9  F~ a G# ` H#  17n 2> 40& &. l *  G# I#  J# K# U M# O# P#t R# n S# g/ T# I U#  V#  W# X#  Y#"  [Z#$ E \Z#( " ^#,  _#0  `#4 < a#8 Z b#<  c#@  d#Dlat f#Hlng g#L h#P ; j#T J l#X mZ#\ t o#` e pZ#d . r#h  13pb21]pb\1n> m> m>.m>m>pboZ0=ja1d@pbc¾ `h!1>#".<>0"=>P#1?Z$pbE!10lb% }>% }>"~>п"u~"~>2% >%f~">R$p#\1&Q1&z1&d1#Z #1#&R&1D#1Z$pbp'! ((), lqrX*1$p+o1,,-,--J , U,oZ-1 >x-1!>|,9]3#qI0\oint8aoZ 1 - HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l    # # E,Z#    ' M!LoS  F_ a G# ` H# 2E$ 6  7 8#~ 9j  )fd *Z# $ +S# # ,S#  7 0 0 f <  O # O !#  "# ## $# ( %# e &#4 2 '#8 ,E 0~5?3555!22234Q5 5  4 22 M4 32-6 X \#  _E# bE# e#  j# P p# G s%# y0# 0# 0# 0# 0# 0# 0# 7&  17n2>40Y  -g .g#db/# 1# ^ 2# 3# 4# 6#$ g/9,#( I:,#* ;,#, 4Z J; %Z+WZ  +2B  2F#srcEF,4%=-aif=.iZR C R /{4Z(0dZT0sZg1'2.R3bH4p5h6|61`7ul1.src#Ful,5Zs-app0dZ10sZD83Ie13I91:.W5.tv6d0srcFj  ;4Z3F>1/ e>2.>2.>s..=p [?-2@cun?F6=reqZ??5ZdA5Zup /'5GZB4D.B4/E :-srcFFY0iHZ:=2IZPCHL:=2eZCj:=2qZ1@:=2YZ$X4Z 3#srcF#pb% !p !s %.%2 %1/%0Z%s.Z%6Z!lenZ%5ZDO444"99Dp444"99"4O4D"9Z"9d9o  )E4 -srcFL-pb% l0lenZ:s.Z23~232.ug21/IN: ; 60e; 3x444596967666 66"8b4445998L444199F34O4D56ZxG :6ZCU{%$ M%%qM%83M4O4D5( 6ZC%&3TZG@ %&3pZGX Y%6{Z:3|ZH|x 45 9G :3Z^H| 45!9G ! :42h:4Z:Z:Z :ZX0t2lZ1t%4ZCF%$ e%%qe%33@!mp4O4D5X!6Z1 %&3hZ ,K6ZLIapp0dZ:0sZN0iZb: Z5x!:.5!0srcF5!:5!tJc2-srcF%-pb% 5!%3.:4 Z:5 9 E_67_x-twp60i8Z/5L`o !-cfK! BKZ+%M% M0strNJ2I OZuT:PZ25QYuX2H Ru\2SZu`2JTZud0twUC "  %$ h%%qh%C& 3 !%$ r%%qr%8; Y s*!4C  P!%$ l%%ql%C  v!%$ v%%qv%8  w!4C  !%$ z%%qz%Ch  !.k{uh1 o 0iZB A ;2SZp U# Q$%$ %%q%KD]$%$ %%q%KD|$%$ %%q%KD$%$ %%q%KD$%2D$4"4D$%$ %%q%KD$%$ %%q%KD%%$ %%q%KD;%%$ %%q%KDZ%%$ +%%q+%KDy%%$ 1%%q1%KD%%$ 2%%q2%KD%%$ H%%qH%KD%%$ R%%qR%KD%%$ \%%q\%KD&%$ h%%qh%KD3&%$ i%%qi%KDR&%$ j%%qj%KDq&%$ l%%ql%KD&%$ n%%qn%KD&%$ p%%qp%KD&%$ r%%qr%KD&%$ t%%qt%KD '%$ v%%qv%K"4,5Z#z+-cf!%: 0strP2I Zu~0iZ12.u~.aifu~2;u~2|6u~2D5u~2y6u~:Z{%j3Z2 @u~%.2 6u~23u~G"e(%$ %%q%C(%$ %%q%C(%$ %%q%G"(0jVZ81=c(4G"(0srcuFC#)%$ %%q%G"e)%23"N)4F4C*)%$ %%q%C.>)%$ %%q%G#)%23# )4F 4CGW*%$ &%%q&%C?*%$ 4%%q4%50#0src5F%H*#P#64<#5#9G#7S#u}6_#7k#u~7w#u~9#6#{7#u~9#7#u~7#up7#u~7#u~7#~7#u~7#u~7$a7$Z3%+4CR+9$8?+4H%4G0%f+9%9%5H%9_%9k%AR3 ZAR+ AB26+An60+xZ+A21+A 327,Aq48,Av29A,3:7LLM,_M- L1 L 6L+ZLoZL Mx3DpM4Etb>9#@WS0\oint8ao- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'M!LoS FD aG# `H# 2E$ 6n  7n 8 ~ #~ 9O  17n 2> 40& & -  .#db /F# 1# ^ 2#  3L#  4R#  6b#$ g/ 9#( I :#* ;#,  <#.key =i#/lat ?b#<  ?b#@lon ?b#D  @#H  B#L  Cy#PP  +F ( H#  K#  L# M#  N# O# ; P# b  y   E  P Q. l *  G# I#  J# K# U M# O# P#t R# n S# g/ T# I U#  V#  W# X#  Y#"  [Z#$ E \Z#( " ^#,  _#0  `#4 < a#8 Z b#<  c#@  d#Dlat fb#Hlng gb#L hb#P ; j#T J l#X mZ#\ t o#` e pZ#d . r#h   Ph# 6 #08 o /p# e9q#  w17xb6yb{P9|b9}bo89Q:. :# 9# :# 9# 2):860s t# u# 7vb# 8vb# u1z#u2~1# 9# 0 # 6#u3P# u4o#"u5#$  a:Y9:5: 6 Z pb f 6.ZL pb-f-ref8i9Z6EZ pbDfDrefOdPqQrcRZiRZjRZ7Z pbfrefiZ27)Z/ pb(f(ref4i5ZG7MZm pbLfLrefXiYZc7Z pbfb8b9b_8brb:b8b79bx7Z3 pbf78:7Z pbf}rc=Z?:TbrTbb8Ub9Ub_8Ub:Vb8Vb79Vb/X7Y f;Z Z7ZI pbfrefdiZ 8bg latb8b lonb!I ##, "[ !g #$X "y #B8$$ $4-%$9Z $%&f0'ffp(9,)sy*.Z*9Z9Z*9Z 9Z*9Zc+q8Z%S'X&ref(Z&f(\7X,)iZ)rr1,`%*e9 )len )r2 #9`''G(4-s(}-x''*x9 +9b'(`(b8b(_8b?(9b_(:b(79b(8b:b.8b[/ab^cb7Zpbf}rcZ01 1 01+ 1 25 2A 01i 1^ 2s 2 2 2 2 2 01 1 2 2 021 1 2 2$ 0N1L 1A 2V 2b 01 1 2 2 2 2 2 2 2 01 1 2 2 2& 01Z 1P 1E 2f 2r 2~ 2 2 2 2 2 2 2 2 2 1 1 2( 24 2> 7Zpbf}f; rbb8b9b_8b:b8b79b/iZ%8 Z(1F&pb  &f C(} c.E: ZuH,%)rc Z3`% 11}1r,&24 --1 1 ---5 U6 7L h&1i 1^ ,&5s U6 G6 6 36 \5 uP7 &1 1 ,&6 6 6& 73 '1Z 1P 1E ,P'2f ,'5r uL5~ \2 2 2 2 2 2 2 2 6 J7 '1+ 1 ,'55 U6A .4 (X) 1 1 -(A)5( U64 m6> 7m (U1 1 , (6 6 6 '5 ]2 2 2 4/ *.+1L 1A -*+5V U6b 7@(1>141),p(6J45V]2`2l2x2226.6b7 ('1 1 ,(5 U6$ %8 ,H-1 1 #l911z&f*9+9Z15&ffp&f0.(9M(\7 .g8b)k)p)iZ*U Z*?9Z*}9Z6*Z:Zj*7/ffu`9)so,0)cKZ%8Z5X@v&ffp (8-/f0{)iZ9)sn*7*7,)ff)f:>>at-b-@%@t3b;8`@@<<=,D=-~ <b<b#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'M!LoS FO aG# `H# 2E$ 6y  7y 8  #~ 9Z   )fd *Z# $ +S# # ,S# 7 0 0f <  O # O !#  "# ## $# ( %# e &#4 2 '#8   ,E X \#  _E# bE# e#  j# P p# G s%# y0# 0# 0# 0# 0# 0# 0#7&   17n 2> 40- .#db/S# 1# ^ 2# 3Y# 4_# 6o#$ g/9#( I:#* ;#, <#.key=v#/lat?o#< ?o#@lon?o#D @#H B#L C#PP +S (H# K# L# M# N# O# ;P# o     P Q. l* G # I# J# K# U M# O# P#tR# n S# g/T# IU# V# W# X# Y#" [Z#$ E\Z#( "^#, _#0 `#4 < a#8 Z b#< c#@ d#Dlatfo#Hlnggo#L ho#P ;j #T Jl #X mZ#\ to #` e pZ#d .r#h Vu Z # Z# Z# &# *   ? v P q } 5  ! fdZ# &# # &Z# Z# * # P # N  # Z#@tio#D # / # ' #  # # 3 # T% C #   #  Z#  Z# R#  # xZ#" . #" WZ#B hZ#B #B     #  3  ZC  S  < # +Z# # # /# F# Z# 3# 0# S #! 0# ;0# 0 # z0 # r Z#$  #( m#,tty#0 Z#4 Q#8S       j  # (#t#  # 4-  # E # =# : # U # # :# # #4 #8  D ' Z# !#   "I;  \N3S TZ# JUZ# VZ# XZ# Y# H Z#Z l]: k ^# _;# x` # Aa# L b# c# e#  f# 5 g# h#' ko#0 9 lo#4 mo#8 rZ#< XsZ#@ 3tZ#D u$#H G wZ#L ]x*#P zZ#T {*#X }Z#\  ~*#` Z#d *#h0Q 4 # o# 9 o# o# 5 o# o# 6# }<# P B# B#$ Z#(  Q#,: G $ 0 H3GW - K][ _; R; # /!#  :"J<'I 2;(# ;)# ;*Z# ;+Z# %;-Z# f;.I# Y ;/; gps# # IZ#  :d:NZ@ X;N #<N!iOZ!midOZ":OZ!lowOZ!gpsP #2"$ X%"qX%$!c3\<Z E+ :%nZ!iZ&:O'%;OZ(dpPY)C;Z@@@*app ):Z@@l*app+;WZ@A,dpW-uW.iXZ.nXZw//Yb/:Zu\0P)(c_1nAA2$ r%2qr%3:BJ34aif35;B4dp6iZ!7ggaM7rmcM~8u]n9Z?9]Zs6p!p26p06s9;ZD8n; i:h)  ;;<)=x===>f1hHH=3:@)?;j;^?R<)=tk:@)r;j;^?R<*=tW@CC1CC" n"Z@CC1CC"  n" Z@yFF1yFF9 n"Z<0*"$"o%6b9 S H ]  nt7A.<BJ7K,dpBB;7@KK-;7=,buf7B.fp: C;<5%S4!i6Z!p7 ;Z%S!iZ#h!crcZ":Z"-Z#!xorZ":Z!c$!xorZ":Z!cCy:a%S!aif3"< "Z"; ";Z#1"$"o%!s#B!dp #S!dp$!dp ):XZKVT4SW 9 Y+!aifZ36c[Z!i\Z:X*;<*=D :+<y;*;<,=D=S=D0H-=G0`-DD=&60-=61VV=T0-26lenZ!p FUUc;<-D d /v;uTG;LZHHI,OI- H &HoZI@5hD<VW]0\oint8ao-i HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#p  i  '2E$6 78  #~9 17n2>R<)tbuftnZcrc)zBV W^T_h<ZWSWbuft*nZUcrc)B--h^<)`WWxcrc) t lenZ7xWWc }<)WWb!cpt""Z#crc)QWW$c %%&,&-  )& &-`8&..O&m<fgF<WW^0\oint8ao-i HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#p  i  '2E$6 78  #~9,- G<W[b_0\oint8ao 0- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'M' V'# E7  &7  Fv a G# ` H# 2E$ 6  7 8  #~ 9 mO'  +V   Y > G  #  G# W !A h / M u  )+ , ../< 23- : ; <Zx 4x \   Z# Z# Z# Z# ' # !# k"# ##=< <# # 0=#ai# sa#,\=WX<' XYpi(Z<.n.pZai/upreq/Hrc0Zx<ZQZH=h`Zd[\iB~<pp[[<np@=Znrp<Z =;,Z!!",v" ! M!oZ"<vaI=[ ^Qa0\oint8ao- HZ# # # # ~# # w# D# # #$ Y#(  #, "#0 $#4 &Z#8 *Z#< ,z#@ 0>#D ]1L#F 2#G 6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l   # # E,Z#    'M F. aG# `H#2E$6X 7X8 h #~99s=OZaNbNiPZ=@ret?a?=?Z=\ZAa[Ab[Adtek=Z[E\0 =\=>Z=ZQ=,P\\ ret+ a+y=+Z<c1ZQ!z\\"=3Z#\\\5$$%#\\b$$( #\]H $t $ &# '\\eSJ!\\= ] ^ =~  tv? '  > > "="=(@.T,#(X.w)x.#'U]b]eSJ!U]b]& *.w).#'b]r]eSJ!b]r]&B ++, ,., -h -now, +o ZKb./tmp/cctYEMTs.s/build/buildd-glibc_2.7-18lenny7-i386-lqGp8g/glibc-2.7/build-tree/glibc-2.7/csuGNU AS 2.18.0% $ > $ > $ > 4: ; I?  &IU%% : ; I$ > $ > $ >   I : ;  : ;I8  I : ; : ; I8 I!I/ &I : ;  : ; I' I : ; I8  : ;  : ; (  : ;.: ; .? : ;' @: ;I4: ;I4: ;I4: ;I : ;I : ;I !4: ;I "#4: ;I$ U%.: ; ' @&: ; I '4: ; I (4: ; I): ; I* +4: ; I ,.? : ; ' @-4: ; I..: ; ' /.? : ; ' I@0: ; I11X Y 2 U3 44: ; I? < 54: ; I?  64: ;I? < % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I : ; I8 & : ; : ;I 8 : ;I : ; (  : ;  : ; !I/ : ;I8  : ; : ;< &I .? : ;' I !: ;I".? : ; ' I #: ; I$.: ; ' I %4: ; I&4: ; I'.1@(1).? : ;' @*.? : ;' @+: ;I,: ;I -.? : ;' I@.4: ;I/4: ;I04: ;I 14: ;I21UX Y314 U5 U6 7 84: ;I9: ;I:.? : ; ' @;: ; I<4: ; I=.: ;' >: ;I? @4: ;I A1UX YB41C41 D41E: ; IF G1X YH1UX Y I1UX Y J4: ; I K4: ; I? < L4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I& : ; : ;I 8 : ;I : ; I8  : ;  : ; (  : ;I8 !I/ : ; : ;< &I.? : ; ' I@ : ; I!4: ; I"4: ; I#: ; I$: ; I % &4: ; I'.? : ;' I@(: ;I ): ;I *: ;I+4: ;I ,4: ;I-4: ; I? < .4: ; I?  % : ; I$ > $ > I!I/ $ >   I  : ;  : ;I8 : ; : ; I8 &I : ;  : ;  : ; I : ; I8  : ; (  : ;  : ; : ; !I/.? : ;' I : ;I.: ;' I : ;I4: ;I4: ;I.: ;' .? : ; ' @!.? : ;' I@": ;I #4: ;I$: ;I%: ;I&4: ;I'4: ;I ( U) *1X Y+1,4: ;I - U. /.: ;' I@0: ;I 1.? : ;' @2.? : ;' @3.: ; ' @4: ; I5.: ; ' I@6: ; I74: ; I84: ; I 94: ; I:.: ;' @; : ;<.: ;' = > ? @41A4I4  B1UX YC41 D41E1X YF1UX YG4: ; I? < H4: ; I?  % : ; I$ > $ > I!I/ $ >   I  : ;  : ;I8 : ; : ; I8 &I : ;  : ; I : ; I8 & : ; : ;I 8 : ;I : ; !I/ : ; (  : ;I8  : ; : ;< &I.: ;' I  : ;I!: ;I"4: ;I# $.: ;' %.? : ;' I &.: ; ' ': ; I(.? : ;' @): ;I*4: ;I+ U,4: ;I-4: ;I ..: ;' @/: ;I04: ;I11UX Y213414 5416 U71UX Y841 94: ;I : ;.? : ;' I@<.: ; ' @=: ; I>: ; I?4: ; I@4: ; IA4: ; IB4: ; IC : ;D E1X YF1X YG4: ; I H4: ; I? < I4: ; I?  J4: ; I? < K4: ;I? < % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I : ;  : ; I8 !I/.: ;' I : ;I4: ;I4: ;I.? : ;' I .? : ;' I@: ;I: ;I4: ;I 4: ;I4: ;I : ;I 1UX Y!1" U#41$41%4: ;I&.? : ; ' I@': ; I(4: ; I)4: ; I*4: ; I+4: ; I , - ..? : ; ' @/: ; I04I4  1 U24: ; I 3: ;I4 5 6 7.: ; ' I 8: ; I94: ; I:1UX Y;4<4: ; I? < =4: ; I?  >4: ;I? < % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I : ; I8 & : ; : ;I 8 : ;I : ; (  : ;  : ;  : ;I8 !I/ : ; : ;< &I  : ; !.: ;' I ": ;I#4: ;I$ %4: ;I&.: ;' ': ;I(.? : ;' @).? : ;' @*: ;I+4: ;I ,4: ;I -4: ;I..: ;' I@/1UX Y011 U2413 4415 6: ;I74: ;I8.: ;' @9.? : ;' I@:4I4  ;1X Y<.: ; ' I =: ; I>4: ; I?4: ; I@ : ; A1UX YB41 C 1D1UX Y E.? : ; ' @F: ; IG4: ; IH4: ; I I4: ; I? < J4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I : ; I8 & : ;( : ;I : ; : ;I8  : ; .: ;' I : ;I.: ; ' .: ;' @: ;I.? : ;' @: ;I .: ; ' I@!: ; I"4: ; I#1UX Y$1%1UX Y&4: ;I '4: ;I ( U)4: ;I*!I/+.? : ;' I@,: ;I -: ;I..: ;' I@/ 04: ;I14: ; I 24: ; I? < 34: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/  : ;  : ; I : ; I8 .? : ; ' @: ; I .? : ; ' I@: ; I4: ; I4I4  &I4: ; I? < 4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I : ; I8 & : ; : ;I 8 : ;I : ; !I/ : ; (  : ;I8  : ; : ;< &I.: ; '  : ; I!4: ; I".? : ;' I@#: ;I$4: ;I%4: ;I&4: ;I'4: ;I (4: ;I ) U* + U, -.? : ; ' I@.: ; I/1X Y 0114121X Y 3.: ;' @4: ;I5: ;I 6.: ; ' @74: ; I84: ; I 94: ; I:4: ; I ;4: ; I<4: ;I=: ; I >.? : ; ' @?4: ; I@4: ; I A4: ; I? < B4: ; I?  C4: ;I? < % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I& : ; : ;I 8 : ;I : ; I8  : ;  : ; (  : ;I8 !I/ : ; : ;< &I.: ;'  : ;I!4: ;I" #.: ;' I $: ;I%.: ; ' I &: ; I': ; I(4: ; I).? : ; ' @*.? : ;' @+: ;I, U-4: ;I. U/4: ;I 0 14: ;I2.? : ; ' I@3: ; I4: ; I 5: ; I64: ; I74: ;I81UX Y91:41;41<.? : ; ' @=4: ; I >4: ; I?4: ; I @ : ;A : ;B1UX Y C1UX Y D4: ; I? < E4: ; I?  F4: ;I? < % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I.? : ; ' I : ; I4: ; I.1@1 41.? : ;' @: ;I4: ;I: ;I4: ;I  U4: ;I 1X Y1 .? : ; ' I@ : ; I!4: ; I"4: ; I # U$!I/%: ; I&4: ; I'1X Y (4: ; I? < % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I : ; I8 <  : ; .: ; ' I : ; I4: ; I.: ;' : ;I4: ;I4: ;I.? : ; ' .1@.? : ;' I@: ;I : ;I : ;I!4: ;I"4: ;I #4: ;I $1UX Y%1& U'41(.? : ; ' @): ; I *&I+: ; I,4: ; I-4: ; I..? : ; ' I@/: ; I 041141 2: ; I34: ; I44: ; I 54: ; I 6 7.? : ; ' @84: ; I? < 94: ; I?  :4: ;I? < % $ > $ > : ; I$ > .? : ; ' @.? : ; ' I@: ; I : ; I 4: ; I 4: ; I  I &&I.? : ; ' I@ U% : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I : ; (  : ; I8  : ;  : ;.: ; ' I : ; I.: ;' I : ;I: ;I.: ; ' I@: ; I: ; I .? : ;' I@: ;I 4: ;I!&I": ; I#4: ; I$.: ;' I@%: ;I&4: ;I'4: ;I(4: ;I)4: ;I *4: ;I +1X Y,1- .: ; I/4: ; I01X Y1 U2 3 41UX Y5 U641741 84191UX Y:4: ; I? < ;4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I : ; I8 <  : ; : ;I8 : ;I : ;.: ;' : ;I4: ;I.? : ; ' I : ; I.: ; ' 4: ; I4: ; I  .1@!1 ".? : ;' I@#: ;I$1X Y% &41'.: ; ' I@(: ; I)4: ; I*1X Y +1,.? : ; ' @-: ; I.: ;I /1UX Y0 U141241 3: ;I44: ;I54: ;I64: ;I 74: ;I8.? : ; ' I@94: ; I:: ; I ;.? : ; ' @<4: ; I =4: ; I? < >4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I& : ; : ;I 8 : ;I : ; I8  : ;  : ; (  : ;I8 !I/ : ; : ;< &I.? : ; ' I@ : ; I!4: ; I"4: ; I #4: ; I$4: ; I %4: ; I& U' U(.? : ;' @): ;I*4: ;I +4: ;I,: ;I-4: ;I..: ; ' I /: ; I04: ; I1 24: ;I3 44: ;I5.? : ;' I@61UX Y718419 :41 ; <41=4: ; I? < >4: ; I?  ?4: ;I? < % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I& : ; : ;I 8 : ;I : ; I8  : ; !I/ : ; (  : ;I8  : ; : ;< &I.: ; '  : ; I!: ; I"4: ; I#4: ; I$.: ;' I %: ;I&: ;I'4: ;I( )4: ;I* +.: ;' ,.? : ;' I -.? : ; ' I@.: ; I/: ; I 0: ; I1.? : ;' @2: ;I3: ;I 4: ;I54: ;I6 7.? : ;' I@84: ;I 94: ;I: U; U<1X Y=1>41?41@: ; I A4: ; IB4: ;I C1X YD1UX YE41 F.: ; ' @G.? : ;' @H.: ; ' I I : ;J K L1UX YM1N 1O4: ; I P4: ; I? < Q4: ; I?  R4: ;I? < % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I&<  : ; I8 .: ; ' : ; I.? : ; ' I .: ; ' I : ; I4: ; I 1.1@1 .? : ; ' @: ; I 1X Y !.? : ; ' I@": ; I#4: ; I$4: ; I%: ; I &4: ; I'1UX Y ( U)41*.? : ; ' @+4: ; I ,4: ; I? < -4: ; I?  % : ; I$ > $ > I!I/ $ >   I  : ;  : ;I8 : ; : ; I8 &I : ;  : ; I : ; I8 & : ;(  : ; : ;I 8 : ;I : ; !I/ : ;  : ;I8  : ;< &I.: ;'  : ;I!4: ;I" #: ;I$.: ;' I %4: ;I&.: ; ' I ': ; I(: ; I)4: ; I*4: ; I+.? : ;' I ,.? : ;' I@-: ;I.4: ;I /.: ;' I@04: ;I1 24: ;I 31UX Y415 U641741 81X Y941:4: ;I;.: ; ' I@<: ; I=4: ; I>: ; I?4: ; I@4: ; I A4: ; I B: ;IC D E.: ;' @F1X YG UH1UX YI: ;I J.? : ;' @K L4: ; I? < M4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I<  : ; I8  : ;  : ; : ; !I/ : ; ( .: ;' I : ;I4: ;I4: ;I: ;I .? : ; ' I  : ; I!.1@"1 #.? : ;' @$: ;I %.? : ;' I@&: ;I': ;I (: ;I)4: ;I*4: ;I+.: ;' I@, U- .4: ;I /4: ;I 0 1124131UX Y41X Y541 64171UX Y81X Y9 U: ;.? : ; ' @<4: ; I? < =4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I : ; I8 & : ; : ;I 8 : ;I : ;  : ; (  : ;I8 !I/ : ; : ;< &I.: ;' I  : ;I!4: ;I"4: ;I# $ %: ;I&.: ; ' I ': ; I(4: ; I).? : ;' I@*: ;I +.: ; ' I@,: ; I-: ; I.4: ; I/4: ; I 0 U1 24: ; I3.: ;' @4: ;I5: ;I64: ;I74: ;I 84: ;I 94: ;I:1UX Y;1< U=41>41 ?1@ A.: ; ' @B.? : ; ' @C.: ;' D41E1UX YF1X YG4: ;I H4: ; I? < I4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/  : ;  : ; I.? : ; ' I : ; I4: ; I&I.1@141.? : ; ' I@: ; I4: ; I1UX Y 1 U41: ; I  .? : ;' I@!: ;I": ;I#4: ;I $4: ;I%4: ; I? < &4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/  : ;  : ; I4: ; I? < 4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ;  : ; I : ; (  : ;  : ; : ; I8 .? : ; ' @.: ; ' @4: ; I U4: ; I 4: ; I .? : ; ' I@: ; I 4: ; I 4: ; I!4: ; I? < "4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I' I.? : ; ' I : ; I4: ; I.? : ; ' : ; I 141.? : ; ' I@: ; I4: ; I4: ; I .? : ; ' @ : ; I! "4: ; I#.1@$1%1 &41'1X Y (1UX Y ) U*1UX Y +4: ; I? < ,4: ; I?  -4: ; I?  U%# init.c /build/buildd-glibc_2.7-18lenny7-i386-lqGp8g/glibc-2.7/build-tree/i386-libc/csu../sysdeps/genericcrti.Sinitfini.c,!/!=Z!gg//^!/!=Z! /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/usr/include/sysaprx.cstddef.htypes.hstdio.hlibio.hstdarg.htime.htime.hpthreadtypes.hpoll.hstdint.haprx.hgetopt.hpthread.hT@ fu. .u.=yNh* -Zh[+?%U?:v<Ety/]8j \$<K-//=/CXrX<=u{$nt=gB3vu_yK=YZZ[(hZ/ -/ g===?Z[' JY[Zv%JZZ~.K-/i;u=u!ZK%zXY-k1=;**&~(t=uMiy /usr/include/usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/sys/usr/include/netinetttyreader.cstdlib.hstddef.htypes.hstdio.hlibio.htime.htypes.hpthreadtypes.hpoll.htime.htermios.hregex.hstdint.hsocket.hsockaddr.hnetdb.hhistorydb.hpbuf.haprx.hin.h<= w'>{f{X.FKL~6%~ t0).W $\ 0"~-GKIMM/e=Y9wz=vڭ;//gd1!ftJ @#g~Cyuv@vd>,0mY& JN}Aj=--//?-//iKD  vfuuvxv>u-/0 A+=v!  8~0=-g&It# 0t[|,,hp@Z[zJBz X0m<{|ʻYlJ<lXvqtgtXa. Xvf ,YXh:?s<XZ tee>?a.us A2:>q Jw4m=I<nmX?K\M?kuhzhtI"<myt/X/ ge=gLg;/^l<e</<>OJ1u,>1Wgi*jzpt(@hg wur?$ <uX/> K1!XʹY?L0 ">.!!!y,t.L;uldh,>gD)",$;=(KYe/0,ZgK+/y>Yg!,KKCJ(3l;u)9 q,K~;bf2s//{/ttC73y+gZh>$Kb%p< 䭭yֻgch=>9?M yXf_yky<>,_:t]B k8x #gKZgK\?9/=M w< fv,00Y;=/xa3v@ZvXnZeghBZ <uuu tyfxwZ,KDy&:]x-//ujD#hr2?U/,hhn.mf.mJ.wZKS:1 nxf9d0vZd\ < wX <wf f;//K#`1׏68>?SvHm ,w>~ #`X$< Xge0~fhui/gd3uf:xZ~ rhh~\f$\f/ Myo3/)2Y;=g].g~:>Mx9ww:00C<<@X<@<Xeȭ=Zu&pJit.uk00JqJut.X~}+9wz4  9Wv>?d8>@hr>=-=;==-=;=0W=/;4O tIYIY 4 /usr/include/usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/sysbeacon.cstdlib.hstddef.htypes.hstdio.hlibio.htime.htypes.htime.hpthreadtypes.hpoll.htermios.hregex.hstdint.hhistorydb.hpbuf.haprx.h d>Kmy./=V vug{QRxXxb@mhH>N9hy[j;=,>;=g024_\)t t~3"4ct~.z"4~*=w<d,LyXmy.gY=e/K%yXmy. t8#>6|eg"gg-/fgn.\p|h=gK>;>:>je=-=g,v>P.=/;>:>je=-=g,v=a伄;g(|tntwQ/s?k# ٭=e0nfh twt .>:>>`K+ KMv*/ǡv*V>m=-//}<}.te*= }"/uhtXtʯ;==;>I=>#gu/iz<1i+=v0;/Y=K{fZgg5!!|j0%t+(3'= t}Lv*d>.|Xx}JKv*v#"Kv*v#>g"Mv*󭄀>/<+K>LqXW~J0U~ .6(f#Q'~Q#,*'>i/Mv*1=v*d>2:>'M'k.M'vy ~td>,>gv*u=f1CDx={a$ <uX;=/>MLWg-=gdX;KrK刯Vp/(=YLl]##,s /usr/include/usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bitsconfig.cstdlib.hstddef.htypes.hstdio.hlibio.hpthreadtypes.haprx.hfgUiY=Y)kuzf Xx6lzJu/+[huhui;=Ky;==V*;=y;==v=Uy.r$f>v=Uy.$A?.kgzU1=: uZtȤM X=;>M^==g>::=KKg=;=;=;th <_1f=u˪.Ah.Y|  ftXhiwwwwyuZZc?v| w##Ju|/Y$ fuXhYh=t t~Ju4ut9ut'Y1:0+z+;/gDu)v烃Y8)v~Jut~9~.u4J~<t~t t>.v.(yyyz.M%u43% % % %%;/l(;YZ]c4ɁgYgx,i. ~=40=*v=_>9J3 /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/usr/include/sys/usr/include/net/usr/include/netpacket/usr/include/netax25netax25.cstddef.htypes.hstdio.hlibio.htime.htypes.htime.hpthreadtypes.hpoll.hunistd.htermios.hregex.hstdint.huio.hsockaddr.hsocket.hhistorydb.hpbuf.haprx.hif.hpacket.hax25.h/=nxf :v <v9[DI<Xf;L8%g=+ege=;=;ww4gө 14JKx/"< fv.-#gz(;"-gh0"$YTt50̕uZd>,01;zzxo~&T@ge0juugd+ ^v<|ȯ%K(0j.?CvXB8;wb3uk7erKLgeu=;u=-/i=Lu=g=~X<Ze/~.}u;uK-u/o( j=Cy.0g=gv6i\,ip$ {U JeYZZ . Jkv%uv %}<z<.,02 $Jd. .-L=L1#h /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/usr/include/syserlang.cstddef.htypes.hstdio.hlibio.htypes.htime.htime.hpthreadtypes.hpoll.hstdint.haprx.h@DzPhV\LVjy.m!-0{t,g $Tji~\DZlt| .f|<t{XL{t00[0u/tVع=y'eg==9>;*/u`<!t_f!_<#f-G03gz[wmW=Kwg=s}+ih MZVgLgXuqt3KLLr0//MJ=e<k<X=jf=<"<=e>Xgj<=<=_XtY=e>Xgj<=<==;=-= .he/vsgTN1o%gYd+ /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/usr/include/systelemetry.cstddef.htypes.hstdio.hlibio.htime.htypes.htime.hpthreadtypes.hpoll.htermios.hregex.hstdint.hhistorydb.hpbuf.haprx.hM. / <uXKL=h-/l==CJ=h-=e<euu -=>9//>;;?W=uhdh+Yh(' d5,l(j%k,|2V>Qg=MS$AaAKLfc./u/uYs//}8 e-gaux׭-gij<f==-gvY tFl<=-gvY tFn<49M8@FLYc@pDo<49M8@FLYc@pDo<49M8@FLYc@pD" dltt tuwu@~H^tޟe-gau/-/&' *ͼlT,<TX1h)w>sx@x@ztgt~䃑]yz\vPmnX; /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/usr/include/sysigate.cstddef.htypes.hstdio.hlibio.htime.htypes.htime.hpthreadtypes.htermios.hregex.hstdint.hhistorydb.hpbuf.haprx.h\@gWɄ//k0wui t{i tw< J2B. -=kgst*!J.?K>J9<G<90fghut< .t<6Y~~ w tn8j fLc7 } KegP):vd  =v< <t< <w. <=>1-=..r}t-=.mtrlfjJ<8w=-=j<f-=1tHmJt+0u%lJ /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/includecellmalloc.cstddef.htypes.hlibio.hstdio.hcellmalloc.hhBC< fw< fQu0;=OGjrX <sJ/ Hf<!wFto<.v1+=h1[J).zfȐg[mYgj>V>u>:> f # /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/usr/include/syshistorydb.cstddef.htypes.hstdio.hlibio.htime.htypes.htime.hpthreadtypes.hpoll.hstdint.hcellmalloc.hhistorydb.hpbuf.haprx.hl<<<<<",0zBx8@:v|<.|<r>fpKLYl|ւgguןexN*\g/dGJgWf=-/0=Vvntt=tZZ=Q/tQfg .u.aJ3YKg~.m<XL%{@}f.}<'8<Ht?g0G.:Dz<=gggI=/hggVL4Bz<4uw'(f~zB Xf-Kf܄"=YJ>dv@5<Gt@i.J<9(/~tKKLY(P7KLYF&'=;u ggghC;zBx.Dz<=ggguI=/h=e>VLf:v"=Y<~.+/B<#~f"=Y)/Ua gH=Y/0,, GXuZ=-\gm9x9 /usr/includekeyhash.cstdint.h}'<a" t;$׭m"%3// <p<.vt.yfCu#;/a=W=\ Jr<yJyJCy/A?A<?.Ftx<IDg[a'#tyJ>p]>'-&+KKmjtjghJ0} <tX VZK f'{<xf{Xx.|$g;=W=g%y L z~# *` 2t.}>VZg'@~̑.:>,hgY-/g@=g}.yJ} gPyJCx<(%BYxr<.rX<rX<g#;:0i;1= J%%$(?9!?}Y;=W= .%*D(u*{J *4yJI=i-}?'XX}^iL>Vv#&wR}fgzh*;%x Jg&;=u;<t{Au ]=;=I?xtu< {P%J|<;=gK4J=~!~YYu;{[ wJ JuV ]u-'91G?/lg;=g=;0gY??9?(fe";/ P "# /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/usr/include/sysdupecheck.cstddef.htypes.hlibio.hstdio.htime.htypes.htime.hpthreadtypes.hpoll.hstdint.hcellmalloc.hhistorydb.hpbuf.haprx.h<=ZXue=E Xg|BzX4,01d/g5MtbXKRq*tt=u;K1dfuwt|}Kg v.d~}Kg֣;=;@8=0<mX9g=Fy~6uZ>,\[fJh9 /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/usr/include/syskiss.cstddef.htypes.hstdio.hlibio.htime.htypes.htime.hpthreadtypes.htermios.hregex.hstdint.hhistorydb.hpbuf.haprx.hd<t. Jty<_=IYum<.%Y=kH=!73WKI0 fhYYizYY=dB=YgYg^0b2ih<h"T/Ki/ehHhgtX,r0m B%t heg# ׃/ehHhg\d"taf)tP~<}f~f<t%VuK&ENX}u-=< }X! zAv0._tVvh -~&&~0 u 82 Jbxg=-M ȹg+KKt>  ."<J)-/h//~ɟvyfC fkt%(~(r&"(EN9Mjb2iZd,>g~(< ,$0&0'&&\t&&Z7*L.'&-#&H- /usr/include/usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/sysinterface.cstdlib.hstddef.htypes.hstdio.hlibio.htime.htypes.htime.hpthreadtypes.htermios.hregex.hstdint.hhistorydb.hpbuf.haprx.h<=fr.h> <t  <=Z>zX4jT=ej*@0w##=;Y;u$L#X0 Q&X3##< ȑCu2ȅ=bi"",?@8?->Wh)2-gz.6K-?*WzK0-]8@\_t!._.UJt?`>*Y=;=+M+F3>8\X)-J/X8*r0y'8*@g˦<-/l?+me=-=/; 2~2/%x;AEP>x.B>/L N.XtjBy0( PfK=MN'3.jP"^{ez.>zmI=WP?:KKK!qQ1=s| Xvf Xv. f/)t vXZ8x:uu/}ȕa3K-g/tf}]V+imGic؆f<fJJ3=rhl=>2c?K/0:h f' G[[yf@y<?xbjltAa3.[=e=,d0X,) .Z]P+img;guGj~@/\u'+KK=:\Z&-/l& _u z}.4..$*}+M &tE.(t J"t#>gUZK0l t!W0x@p<)guuggL=m<<}<<}t<}<<}/ <uX1Dx.Y;=[!V>~<p w< fuL4<Ltd<'X1U|1&2=}(LgI=MJ<<{0r!3 t}x%|L7-!#zJu-KZY{JRK,~((Z~,g'Lv< f~,/;/R=;==Oa=u;gLdM;v"u;uw\=;=ڭ;==!-=gg0<~?=-=='{JRK-/;=W= <>}ug,~,|/:'Ƀ;Y sg=W fjZ:0gp<=g0/v!eg$v< <y=s/<;=C*.=t,<;==uO#{(%=///uvU=eg-=YT{{.Z[t>u;K0Lf0Uf;y.QpJɹ/$G==Y*  f`< -/./JDx< Cx<=?A2)>wG=K=K9=vT x;.~j9 8 /usr/include/usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/sysdigipeater.cstdlib.hstddef.htypes.hstdio.hlibio.htime.htypes.htime.hpthreadtypes.hpoll.htermios.hregex.hstdint.hhistorydb.hpbuf.haprx.h Zd>Kg-IG?ȓto<.=-=lmX>d>=t~^pX!vkw:=u:>s< <sf <sf9]zs,>0'y{ WCy;;==KK>>J Hh .g?:/=+w/=YM>:/==g>;;==K=/:/g=ug>MP >;;==KK>goi=Iug/X9[.! Xb82xȄ- 82w< .n-e82-Q$-fxXj*njKyty " =HZJw[Z4g-=/6i =_yfyX0 y8y$uy.mkGx/Y&~Ldhg/35B!a(-/j~ȟ}$XKuR.u#(q.tfgh+=ghd0,h2/Y=;jKYgib1+ij^KLvJz v*0 L><ɾ ֆ~֭=~.y</J "xv#D~.<~t=~.0ix٭v=;pX>>,\t*6.u=/X+U<w~JQ1U?.L;KBz<'/-/nh4D ˑ8H8XH<X&L   =;=-=lc?+1Z+]gwKu;=u<p<<- /?e/[󄭢P}J1<O<6 >=;0;>;>;>;=?J t}'sG=Pf3.:O<3fKe=y$e=Zu{.zXz u.9> J?9//%2Vu((I7If7Xf%%57#?B40v>vX#?sw?X<m<mf= / fuXi/[h==h-gf<f&hud !{Kح.AX5?>rhw/ge;@:0yJlz< .kaNg/>Lg>gcAU=>urY~ ?vYzzfXg#2++<uh1YzzfXg.*h+<+5uk1-gg7}tcu t1Kg/ fuX1Dx.Y;g[YVh=-g3s +5|<|f!?>Y|'|fXg22%=%~;>dL=hd==?b>h:?/;Ky># <y?9} /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/usr/include/sysfilter.cstddef.htypes.hlibio.hstdio.htime.htypes.htime.hpthreadtypes.hstdint.hcellmalloc.hhistorydb.hpbuf.haprx.h#ffA 't XK yJBK:LKvv:vKv"L=3)Az.KObt "vu~a? vgNU[MthiV%s"s1.M#VHLgzJDxtu;=C;u=j.Kgfz.;uSJ4euZ}M{t;u<.|Ju;uX|t  }J~.{4s/.~.&|0zg<u/;/KY=[t(-/guKȄ/;v< h-YB  6$=I=v-;/YY-g3ght!Y|.tu;/0+f/|euzJB0!+lu4 t-ggYu YY*}":vF8Y}Wugy1",2~"'5u4;/hf~X}e+|@d"^6"g .d.}63 $axj 9( /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/usr/include/sysdprsgw.cstddef.htypes.hstdio.hlibio.htime.htypes.htime.hpthreadtypes.hpoll.htermios.hregex.hstdint.hhistorydb.hpbuf.haprx.h@",0",0x?/L f;unvefn.u91`t s  k~f,f~Ke=Kq[yS3uY gja=@?f fK;)-SAReu}9gKg(4L#8J1IQ-gUKx zV?)""+{jxYgouurK-//w:>Z- |<<.<|f=ff|f=<<2<-=j  e#Y/>:ggZ5|f#Y=;g|P9w7A)4,Q*JK>:hug|t8x/|8|f=-uI=-=,A7A))*cw9?d;//D~|\=>:hky.>Al>>-/m:#&SALe<<0ɠ/>d/I<~}/&K>:huh%z%=s0X/;/}K&K%&=;=hu(.  /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/includecrc.cstddef.htypes.hlibio.hstdio.hpthreadtypes.hstdint.haprx.hV#+1+X?Ow  .w. .w  .Jw$[w>VJW/gYgh=-[ P /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/includetimercmp.cstddef.htypes.hlibio.hstdio.htime.htime.hpthreadtypes.haprx.h[t=/;0q7&x t=LK=mdPJ#XvP& /tmpcctYEMTs.sX!!!^-!!!| uAB F`AB D&AB FI :AB D`AB D9AB D,@rD  F AB AECAB D| TAB pAB ZAB ?AB  AB F0AB FAB EpAB LPAB FF@AB F@VAB FAB F|  `EAD ADDAD AGAB MI `AB aMI|  pAB  $IB  4AD D 4AD D @AB D @AB F `~AB IL dAB E P,AB  4AB  dAB  0wAB AC WAB BN AB I AB NL yAB I PrAB F|  AB E $AB AFF  AB F 8AB AG %AB F 0AB F AB F  \AB F| 8yAB C8AB JI8%AB C8AD F8AB BI8 AB F8AB FF8PAB 80PAB 8#AB F8VAB 8AB I| /AB  /AB LFF1AB I7AB c:%AB :AB F;AB E<@AB I@AB EAAB DBAB LI|  @DLB A D AD  DAB D EAD D PFAB I IJAB  PIAB E IAB F  JAB FFF LAB | H LAB H L AB DH @LAB EH LAB |  MAB F QAB E pRAB AG SmAB I Y[AB I `[AB E `\qAB |  \AB  \5AB KJ 0^AB F @`*AB O pcAB LF dAB F|  hAB  hAB  hMAB  @ieAB ADD iAB I pkqAD BF k\AB D PlAB F| | lAB | lAB | mAB | mAB | mAB | 0mAD | @m4AB F| nAB E| `ocAB AG| oUAB N| 0pOAB D| p+AB F| q7AB F| uFAB F| @|"AB | p|bAB F| |^AB D| @}AAB | }AB }7AB D }EAB ADD| 0~AD APfAD JAB BC&AB AD AB ACC@GAB F|  AB yAB D xAB LfAB DAB FРAB FУCAB F cAB DAAB | AB FAB BF}AB NI z AB I| t.AB tиAB t$AB  t mAB KIHtAB It AB Ft QAB FtAB IIt- AB ItAB AtAG tAB I| 0 AB @PAB DAB FL0<AB IIpAAB | GAB BAB FsAB FrAB FAB L"AB FAB IAB F PAB FFF_AB AF`AB Fp iAB IAB I| @#AB @#AB @$AB @ $AB O@%AB F@`'UAB D@'AB @(AB F@1BAB E@1AB F@5 AB I@`@AAB | @AD @AD @"AB FBAB IJWAB A @KmAB FFFK AB F| V9AD CDWCAD CF`W9AB F W:AG ADD| $W4AB $ XAB F$ZQAB $`ZAB G$p[uAB E| [UAB DP\MAB D\AB \AD \5AB   ]AB FLFGNU C 4.3.2/build/buildd-glibc_2.7-18lenny7-i386-lqGp8g/glibc-2.7/build-tree/glibc-2.7/csushort unsigned intshort int_IO_stdin_usedlong long unsigned intunsigned charinit.clong long intcan_clear_timereset_IO_read_ptr_chaintm_hourmyloc_latsyslog_facsize_t__align_shortbuf__sizesyslog_facility_nameverbout_IO_buf_basetm_zone_IO_backup_basedeltadebugfac_codepthread_attr_t/x/scratch/mea/ham/aprx/aprx-trunk_fileno_IO_read_endmyloc_coslat__builtin_va_list_IO_buf_end_cur_columnlog_aprsis__quad_tdouble_old_offsetreventstime_resettimetick_countswversionPTHREAD_CANCEL_DISABLEtm_monPTHREAD_CANCEL_ENABLEpollfdpollsize_IO_markerstdintm_year__suseconds_t_IO_write_ptr_sbufaprx.ctm_isdst_IO_save_basepidfile_lockrflogfile_flags2timevalstdoutusageaprsis_logintm_minoptargtv_nsecmyloc_latstrtm_ydayaprxlogtv_sec__gnuc_va_list_IO_write_end_IO_lock_t_IO_FILEpthr_attrs__off_tstderrfloat_markerscfgfilepthread_tfd_nonblockingmodepollcounterlangouttimebufswnametimespecerlangsyslogaprx_syslog_initmycalltm_gmtoffmyloc_londone_onceaprxpollstv_usectm_wdayforegroundsyslog_facility__off64_t_IO_read_base_IO_save_endtocall25tm_mday__pad1__pad2__pad3__pad4__pad5__time_t_unused2tm_secargvstatusold_tickaprsis_threaduint8_ttimeticksig_childprinttimeaprxlogfilesyslog_facs_vtable_offsetargcmyloc_lonstr_IO_write_baseversionprintsig_handlernext_timeoutsrcnameIPPROTO_EGPis_tracesa_familyhistorydb_lookupsttyreader_pulltextc_cflagdstcall_end_or_ssiddstcall_endai_addrlenc_ospeedhistorydb_noposcounttranslateIPPROTO_MTPttyreader_prepollrdlinelenai_flagsLineTypeSOCK_RAWIFTYPE_NULLpacketbufhash1IPPROTO_ENCAPIPPROTO_ROUTINGai_nextIFTYPE_SERIALttyreader_linesetupregs_allocatedfastmapttyreader_linereadttyreader_parse_nullparamsax25callc_iflagatoidigi_relaytypeai_canonnamesrc_ifdigisourcecountdprsgwentrycall_leniftype_ehistorydb_insertsatoldestinationregsIPPROTO_UDPttyreader_getcpacket_lenvia_path__nptrSOCK_RDMdstcall_lenkeylenread_timeoutviscous_queue_spacere_nsubc_oflagwrlensourceregswrcursor__s2_lentelemeter_newformatdupecheck_dblast_heard_bufcc_tdupecheck_tttynameIPPROTO_IGMPIFTYPE_UNSETlast_heardSOCK_SEQPACKETtelemeter_to_rftcflag_tnot_eolt_expmsg_path__socket_typeax25viapathIPPROTO_IPsrc_traceSOCK_STREAMdeltamsdupe_record_tIPPROTO_PIMwait_untilreg_syntax_tIPPROTO_IPV6historydb_cellgaugec_lineaprx_interfacehistorydb_hashmatchesfilter_taxaddrifgroup__socklen_tdigisourcesserialport__s1_lenIFTYPE_TCPIPseen_on_transmittersmack_probeinfo_startax25datalenKISSSTATE_SYNCHUNTttyreader.cdataregscountsmack_subidsdataregsis_aprsIPPROTO_NONEtx_okre_pattern_bufferrdbufttysrdlinesrc_tbf_limitinitstringdupecheckerrdtimeIPPROTO_DSTOPTSIPPROTO_ICMPIPPROTO_ESPkeylenshistorydb_tIPPROTO_MAXIPPROTO_HOPOPTSIPPROTO_RAWax25addrlenai_familyai_socktypeviaregscountinitlenIPPROTO_RSVPttyreader_serialcfgLINETYPE_TNC2IFTYPE_AX25cos_latdstname_lenttycountDIGIRELAY_THIRDPARTYc_ccIPPROTO_ICMPV6qconst_starthas_faulttxrefcountpositiontimeparentinitlengthKissStatelinetypesource_if_grouppacketlensyntaxDIGIRELAY_DIGIPEAT_DIRECTONLYIPPROTO_FRAGMENTdonecountoptsERLANG_RXno_subhexdumpfpreqcountviaregscan_be_nullmarginpbuf_tsrc_tbf_incrementsourceregscountttyreader_inittermiosseqnumIPPROTO_TPLINETYPE_KISSSMACKc_lflagERLANG_TXai_addrparlennot_bolrdcursorarrivaltimeLINETYPE_KISSFLEXNETparam1viscous_delaykissstatealiascountSOCK_DGRAMmsgviapathregex_twrbufIPPROTO_SCTPsrc_filtersIPPROTO_PUPsa_family_tc_ispeeduint16_tttyreader_postpolllast_read_somethingaddressesmaxreqIPPROTO_IDPtokenbucketsa_datalinenum_isockaddraddrinfofastmap_accuratenewline_anchordigi_like_aprsspeed_tsrccall_endIPPROTO_GREplenLINETYPE_KISSBPQCRCttyreader_linewritettyreader_parse_ttyparamsDIGIRELAY_DIGIPEATttyreader_pulltnc2linenumhistory_cell_tIPPROTO_COMPttyreader_registerttyreader_new__resultSOCK_PACKETdigipeaterdigipeater_sourceDIGIRELAY_UNSETaprx_cfmakerawdelayed_seenmaxdonerdline_timepoll_millis_tvnax25pax25datahistorydbsrc_widepacketdestinationregscountrdlenpoll_millisLINETYPE_DPRSGWIPPROTO_IPIPsrc_relaytypeiftypeIPPROTO_TCPuint32_tsrcname_lendstnamerdspacetncidnetax25ttycallsigntelemeter_to_isIFTYPE_AGWPEviscous_queue_sizeconfigfileLINETYPE_AEAbaudIFTYPE_APRSISnkeysERLANG_DROPviscous_queueLINETYPE_KISShistorydb_keymatchesai_protocolKISSSTATE_COLLECTINGKISSSTATE_KISSFESC__s1IPPROTO_AHax25_format_to_tncframelenseen_spaceparse_ax25addrssidflagstnc2buflenax25_to_tnc2tnc2bufmarkflagui_pidcmdbyteportnametnc2addrlendestax25_to_tnc2_fmtaddresstnc2lenframeaddrlenpassaprsis_startMSG_RSTMSG_PEEKaprsis_readupfail_outaprsis_prepoll_qtypeaprsis_initMSG_CTRUNCrdbuf_lenfilterparamaprsis_hostrdlin_lenMSG_WAITALLdefault_passcode__retvalgwcallerrstrpipesaprsis_loginidaprsis_set_filter__PRETTY_FUNCTION__aprsis_set_loginaprsis_downaprsis_configtoutMODE_TCPMODE_DTLSMSG_EORMSG_OOBaprsis_closethennewlenserver_socketaprsis_postpollaprsis_comssockreadAIShindexAIShline0aprsis_add_serverMSG_SYNtextlenaprsis_mainAIShcountMSG_MOREaprsis_stopaprsis_queue_aprsis_cond_reconnectMODE_SSLaprsis_modeaprsis.caprsis_set_heartbeat_timeoutwrbuf_curMSG_DONTWAITerrcodesigs_to_blockaprsis_queueMSG_PROXYnext_reconnectMODE_SCTPlast_readMSG_CONFIRMMSG_NOSIGNALMSG_FINheartbeat_monitor_timeoutserver_nameserver_portaprsis_tx_msg_headMSG_CMSG_CLOEXECaprsis_prepollaprsislogincmdaprsis_sockreadMSG_ERRQUEUE__valgwlenaprsis_upaprsis_runthreadaprsis_postpoll_aprsis_reconnectwrbuf_lenAprsISMSG_TRUNCMSG_DONTROUTEaprsis_sockreadlinerdbuf_cur__len__sigset_tmsg_exec_readbeacon_nowtimefixmsg_exec_filebsetdestaddrmsglenbeacon_msgsall_interfacesfilenamefix_beacon_timebeacon_childexitexec_piddestlenbeacon_setbmsgfree_beaconsetexec_buf_spacebeacon_incrementbsets_countexecfilebeacon_msgs_cursorbeacon_postpollbeacon_msgs_countfree_beaconmsgbeacon_configmsg_read_filedestbufexec_deadlinebsetsexec_bmbeacon_resettimerbeacon_prepollsrcaddrbeacon.cbeacon_cycle_sizedev_nullexec_fddiscard_bmbeaconmodetxtlenexec_bufbeacon_resetbeacon_nexttimebeacon_itexec_buf_lengthall_interfaces_countlenpseen_minusdprslogfileconfigline_is_commentintvlconfig_STRUPPERconfig_parse_booleanvalidate_callsign_inputbufplonpreadconfigconfig_STRLOWERcfgparamvalidate_degmin_inputerlanglogfileerlang_backingstorelatpscan_intlogging_configconfig_SKIPTEXTstrictconfig_parse_intervalerlanglog1minendcerrmsgmaxdegconfig.cconfig_SKIPSPACEresultprestpreadconfiglineifr_ifrnIFF_PROMISCsockaddr_ax25IFF_UPifr_ifrusll_hatypeax25lenifcallsigntx_socketis_ax25ttyportifru_slaveax25rxportsax25ttyfdsmsg_namelenmsg_flagsifreqnetdev__caddr_tax25bufrcvlennetax25_devmem_startsll_pkttypenetax25_sendax25rxokifru_mapnetax25_openslavefdnetax25_initIFF_MULTICASTnext_scantimepty_masterIFF_ALLMULTIdiscard_read_fdax25_addressnetax25_addrxportmsghdrmsg_controllennetax25_devcountsll_familyIFF_SLAVEaxdatalenifru_flagssll_protocolrx_protocolifru_datasax25_familysll_ifindexdevnamenetax25_postpollsllsizeiov_baseifru_netmaskaxdataifrn_nameifru_mtuIFF_BROADCASTrx_socketIFF_LOOPBACKnax25IFF_DEBUGnetax25_addttyportnetax25_sendtoiov_lenIFF_AUTOMEDIAscanmasterfddiscax25ttyportsaxaddrlensax25_callifru_newnamemsg_nameifru_ivalueIFF_NOARPsll_halenrxbufmem_endnetax25_devsIFF_PORTSELifru_broadaddrIFF_MASTERnetax25_startifru_hwaddrifru_dstaddrpty_slaveIFF_NOTRAILERSnetax25_resettimerIFF_RUNNINGmsg_iovIFF_POINTOPOINTifru_addrnetax25.cax25devbase_addrmsg_controlifmapax25ttyportscountiovecnetax25_prepollsockaddr_llnetax25_ptyscan_linux_deviceserror_exitIFF_DYNAMICsax25_ndigisnetax25_openptyax25rxportscountsll_addrmsg_iovlenrxsock_readtitledo_createbytes_per_minutee10_maxpacketserlanghead__subportlogtimeErlangLineserlang_setbytes_rxdroperlang_time_ival_1minpackets_rxdropErlangLinesCountadd_counterlang_adderlang_backingstore_growbyteserlang_filedummyerlang_time_end_10minbytes_rxerlang_timer_initlast_updatelineserlang_backingstore_openErlangHeadlinecountstart_timemsgbufErlangModeerl10mSNMPserver_pidpackets_rxerlang_time_end_1minerlang.cerlang_time_ival_10minerlang_time_endrefpbytes_txalign_filler__pid_terlang_mmaperlanglineerlang_postpollerlang_data_is_nonsharedpackets_txerlang_findlineerlang_rxtxbytepkterlang_capae10_cursorerlang_backingstore_startopserlang_starterlang_prepollerlang_initaprxpolls_millisaprxpolls.caprxpolls_newaprxpolls_freeaprxpolls_resettelemetry_labeltimeerlcapabeaconaddrlentelemetry_labeltxtelemetry_resettimetelemetry_resetlabeltimetelemetry_timesourceaiftelemetry_intervalbeaconaddrerlmaxnewrftelemetry_configtelemetry_labelintervalt_idxsource_counttelemetry_datatxtelemetry_10min_stepstelemetry_startrftelemetrycountrftlmtelemetry_postpollsource_aifrf_telemetrytelemetry_prepolltelemetry_seqtelemetry.crftelemetrytelemetry_paramstelemetry_labelindexheadlenredo_frame_filterigate_startdirectionigate_from_aprsisigate_to_aprsisok_to_relayforbidden_to_gate_addrrflogpick_headsistxfromcalltnc2_forbidden_via_stationidcolonidxtnc2_forbidden_source_stationidaprsis_interfaceigate.cheadsbuforigtocallaprsis_commentframestrictax25_discard0verblogqcodeheadscounttnc2_forbidden_destination_stationidstaroktnc2_verify_callsign_formatstrictax25__s2cellarena_talignmentminfreearenanamecellfreemanyfree_tailfreecountcellblockscellmalloc.cnew_cellblocknumcellsfree_headcreatesizecellmalloclifo_policycellmallocmanycreatekbcellblocks_countcellfreecellinitclientptr_to_cellheadarraycellhead_to_clientptrhistorydb_dumpkeybuf_dbsvaliditytimehistorydb_cellshistorydb_nointerestnext_cleanup_timehistorydb_insert_heardlastposition_storetimehistorydb_initinsertallhistorydb_freehistorydb_dataupdatehistorydb_cellsizefoldhashhistorydb_allochistorydb_postpolltop_interfaces_grouphistorydb_cleanuphistorydb_prepollhistorydb_inserthistorydb_dump_entryhistorydb_cellalignhistorydb_noposhistorydb_insert_cleancounthistorydb_lookupexpirytimehistorydb.chistorydb_keymatchhistorydb_atendhistorydb_newisdeadhistorydb_hashmatch_dbs_countkeyhashkeyhashuckeyhash.ckeyhash_init_ISgraphoverlay_ISspaceparse_aprs_telemsublengthparse_aprs_compressed_ISalphalng2_ISdigitiswestparse_aprs_micelat_minlat4_ISalnumparse_aprs_uncompressedissouthposcharvalid_sym_table_compressed_ISblanklat_deg_ISpunctparse_aprs.cposbufget_symbol_from_dstcalllng1lng3lng4paclenparse_aprs_messageis_acklat3parse_aprsparse_aprs_objectpos_startlook_inside_3rd_partylngpposambiguitynumberidlat_min_fraglng_hemisym_codeparse_aprs_nmealat_hemibody_lenget_symbol_from_dstcall_twocharmsgidlng_degtz_endaprs_message_t_IScntrl_ISxdigitsym_table_ISlowerlng_min_fraglng_minbody_end_ISuppermsgid_lenparse_aprs_itemis_rejvalid_sym_table_uncompressedd_start_ISprintpbuf_fill_posdupecheck_db_freedupecheck_cellsdupecheck_putdupecheck_cleanup_nexttimeduperecord_sizedupecheck_resettimedupecheckers_countdupecheck_newdupecheck_aprsdupecheck_cellgaugedupecheck.cdupecheck_postpolldupecheck_getdupecheck_pbufdupecheckersduperecord_aligndupecheck_initdupecheck_db_allocpktlendupecheck_cleanupdupecheck_prepolldataendkissencoderkissspacekiss.cxorsumkiss_pollcrc16kisslenkissprocesscrc16_tablekissbufpktbufax25rawkiss_pullkisssmack_okcrc_flex_tablekiss_kisswritecrcflexax25rawlenack_messagehist_txviaindexsrcifaxaddrbuftnc2buf1dstname_is_myselfvialendigitxbuftx_aiftnc2datainterface.ctnc2len1ax25addrlen1recent_timeaxlentnc2addrlen1ERRORMEMFREEinterface_is_telemetrableviafieldmaxsubifax25buf1digisourceax25len1filter_packettypefind_interface_by_callsigninterface_default_aliasesdigisrcinterface_initifaddressaifpboolfailtnc2datalentxlengwtypeinterface_is_beaconableinterface_transmit_beaconinterface_receive_ax25from_igatefilter_discardaxbufinterface_configrx_analyze_3rdpartyprocess_message_to_myselfdefined_subinterface_countinterface_transmit_ax25config_kiss_subiftoaprshave_faultdiscard_thisrecipienthist_rxinterface_receive_3rdpartyinterface_storefind_interface_by_indexpath_startsrc_endpacket_endpbuf_cellspath_endvia_startpbuf_getpbuf_free_pbuf_newinfo_endpbuf.cpbufcell_alignpbufcell_sizepbuf_allocpathlenpbuf_initpblenpbuf_putREG_EBRACKmatchlendigistateREG_EESCAPEreqcREG_ESPACEstattokenbucket_timersourcecalltickdigipeater_receivewidewordlensdigipeater_config_sourceatofistraceregex_filter_addparam2free_sourcetracewordlenssource_wideREG_ESUBREGlastviastarREG_ESIZEdefault_trace_paramREG_EENDatermdefault_wide_paramREG_NOMATCHfixalldigi_countdigipeater.cdupestoretimeratelimitmaxgroupcodesource_is_transmittercount_single_tnc2_tracewidenewssidviastatedigidonematch_aliaseswideparamtracereqregexsrchcellREG_ERANGEjitteryis_uidigireqerrbufREG_EPARENdecrement_ssidREG_BADRPTparse_tnc2_hopshopsdonewidewordsrun_tokenbucket_timersfixthismatch_transmittermatch_tracewidefieldtyperateincrementmaxdigipeater_find_dupecheckt2l2digipeater_receive_backendhopsreqtxifkeywordstry_reject_filterstracedonesrcratelimitREG_EBRACElastviacharprobably_heard_directREG_NOERRORdigipeater_configREG_ECOLLATEREG_BADBRdigipeater_config_tracewideREG_BADPATREG_ECTYPEhasHflagdupedigiokdigipeater_prepolltraceparamdigipeater_resettimeREG_ERPARENsource_traceremcdigipeater_postpollfree_tracewidetracewordssrcrateincrementactiveviacounttaillentextbuff_coslatfilter_head_tnegationfilter_process_one_afilter_process_one_bfilter_process_one_dfilter_process_one_ffilter_process_one_gfilter_process_one_ofilter_process_one_pwildokfilter_process_one_rfilter_process_one_sfilter_process_one_tfilter_process_one_uf_latNf_latSrefbuffilter_process_onesymtabledummy2callsignlendummycf_lonEfilter_lat2radfilter_refcallsign_thist_lookup_intervalfilter_preprocess_dupefiltercoslat1prefixbuffilter_match_on_callsignsetrefcallsignsfilter_processsindlon2filtnumnamesfilter_parsefilter_cellsizefilter_lon2radsymcodefilter_initfilter_postprocess_dupefilterfilt0filter_parse_one_scoslat2wildcardfilter_cellalignMatchPrefixreflenfilter_freehistrefmaxlen2len3len4len5len6maidenhead_km_distancef_distlon1filter_parse_one_callsignsethist_agefnextf_lonWlen1sfilter.csindlat2len2slen3ssymolayMatchWildrefcallsignMatchEnumrangeseen_acceptbitflagsextendMatchExactfilter_cellsdprsgw_rxigatedprsgw_newdprsgw_nmea_igatedprsgw_history_tgps2aprs_syms_tcsumexpirydprsgw_pulldprshighfieldsdprsgw_postpollparse_gps2aprs_symboldprsgw_ratelimithistorylimitggalinedprsgw.cdprsgw_prepollgatedgpsxxxdprsgw_historyaprssymgps2aprsSymsggaspacealt_feettnc2bodygps2apr_symsdprsgw_isvalidtnc2bodylenidentdprslogstamprmclinegps2aprs_syms_countrmcspacedprsgw_ttnc2addrdprsgw_nmea_splitaprssymboldprsgw_flushdprsgw_receivedprs_gwcalc_crc_16calc_crc_ccittcrc_ccitt_tablecalc_crc_flexcrc.ccheck_crc_16agwpesocket.cnetresolv_threadhostnamenetresolv_startnetresolv_addresolve_allnetresolver.cnetresolv_die_nownetresolverRE_RESOLVE_INTERVALre_resolve_timenrcountnetresolv_runthreadnetresolv_stoptv_timerdelta_millistimernametimecmptv_timeradd_secondsnowminus_targetnowplustv_timeradd_millisdeltausecdsectimercmp.ctv_timerboundstv_timercmpresetfuncresetargdeltasecttuukWkmumWuuWamPP+iSmSdStt utt6u|~P@AtACtCzuLXPrzPttuSuSu !t!#t#YuPPP&S(YS`dtdjQjktkmtm u`qV q V J qJ V q`qS q S J qJ S q PU]PPuwP SJ O PpD Wj W_ m PA T VT VwSX d S J S SA Vj VX d V V VS S Pi SS S S ul ul J ul ul ulX d uls ul J ul ul t t # u   ! S! # u  P # PttTuTQ`atactceupqtqstsupQpu ttuttu<<u <<ruru`uu`uu`u<SP<S@KSK{P{SPSPSS.S<W>W@W !t!#t# u u  u = = u  >>mumVSuS" u" = S= n Vn S u t t Z u` a ta c tc ; u` } } ; u `  " u" 4 up4 V u V u ; V S ; S@ A tA C tC ' u@ Z Z S u S u ' S P Pl { P V P ' V0 1 t1 3 t3 $u0  u$u RR S S#SkS\S_P&9PPPPW$W S ISS#SkS(SSPP01t13t3u0VVSWSUuUWu0EEup0MMul0P PuhXQSUbS]RVUbV+RUbRttLuLuWSWLWVLVStSSLSPLOP]dPR RJRRRR*RSt SLSSLSttEu\\uEu@V@CQCVQ2V2EQAWACuCEWgQQ9CRtRR R ERNR\tRRRRPQtQUtU!uPccVuVuV!uPccSu SSS!u Pu!S Q!QcfRfrPrRSRSRSPSPP PXWWW01t13t3u0u0KKVu 9VJlVVu jVV0u0((juju0((juju0u$uh(uh9JVlVuhjVWWWWQWW7Q7WQul"ululSRPSPRPSRSS.R.7S7[R[SRSRSRPRPRPSup2upupttuvvuvvWuWpSSttu )t)+t+Du&:RPQtQUtUu_yRnSttuRSttukkukku tt u//Su8S8<u< SRJZR~R/eu\eu@u\u`u@u\Ru@Ru`<u\<lu@lu`u@Uu\Uu`u@u\u@5u\Ju`u@E u\E q u@q u\ u\ u\ t t n u & P& Q VX n V/ N SX n Sp q tq s ts up  V u P P P S t t u P  t  t D uP Q tQ S tS u P $ P P t t 7 u P ) S, 7 S R * V, 7 V@ A tA C tC u@ M PM V V ZV]V@ ^ R^ S S KS@ Q P Q Q KQ@ d d R u KRKu@ zz u KKu @ d d W u WSW [W]WPKYS]S| ux;ux S SPttQuPQugupRupGbupbRRRRRup!R!3up3QRS\PRSS!P!3S3GPGQSSG^SPThPPPS^SSGSWW%xWWW!4WBGWLQWugGugQug`atactcYuP>Qu!YuWYWVYV`atactcuPVVHvVV6VG}VVVVu~u~VSS6SS6CPVWW6WWRxRPRGMP+eRSS6SSttRuVu RVqSquRS Ru--uRuPV{Sttu?V?AuAVJPoRttuPiVitQtVQVQVQRpu`pRRcQtt uP u@R uQuH}uHuHs uH8 T uHo uHIQuLuLauLuL K uLy uLo uLVQuPQiV}uPuPVuPR s Vs uP8 T uPo uPY}uTuTR uT8 T uTo uTxzPQSSR s SQuX}uXuXauXs uXK y uX8 T uX Qu}uuaus uK y u8 T uIQu\u\au\u\ K u\y u\o u\IQuuauu K uy uo uIQuuauu K uy uo u C SC J PJ x Sx } P} S S t t (u P W (W7 S S V (VE J P P01t13t3Uu0uuUuZVUVP<EPPPZWW>WSiWz,W=UWZSSS:SSzSSUSacPSzSZSSSS4zSS=S'R`atactc"u`u"VSPP01t13t3D(u0JPJD(u0JRJD(u0JQJSuh@S@ZuhZrSruhSuhwSwuhSuhS*uh*9Suh5S5nuhnS uh ;S;MuhSuhkvS~S~uh<S<TuhTSuhtS: O uh !S!!uh!!S""S""uh"|#S|##uh#6$S6$C$uhC$$S$%uh%`%S`%%uh%%S%&uh&I&SI&l&uhl&&S&&uh& 'S ''uh'F'SF''uh''S'(uh(5(S0FFuuu*u*__uJuu55nun;;MuMeeu!!kukut: O  !"D(0u D(u@5ku@}D(u@jupjWupWup5eupeWUupUkWWupWtupt: W: O upO W !up!"W"D(upuD5kuDD(uDuH5kuHD(uHuL5kuLD(uLuP5kuPD(uPulWulWulW*ul*<W<ul5 ul ;W;ulWkul\ul\tWt ul !W!!ul!!W!#ul##W#j$ulj$$W$$ul$%W%'ul'(W(D(uluT5kuTD(uTuX5kuXtVuXVj uXj q Vq !uX!!V!M"uXM"T"VT"D(uXu\Su\5ku\vSD(u\ru`rSu`Su`*S*u`5FPFnSn;u`;GPGMSMku`D(u`ud5kudrD(udP(Q(tQ(S(tS(+uP((()S)?*u?*6+S6+:+u:++S((u\((uH(!)u\!):)u`:)T)uHT))u\)?*u`?**uH**u\*:+u`:++u\);*SttyukSkououSyu lVlouovVvyu mWmouowWwyutt]uVuVVVnPVu&P]u up up]upul5ul]ulPR=VP~RP6LRV]RRWR(R*pWp~R~WR(W(6R6]W`atactcu`ttSuSvWzW|WvVPPVP"V".P.0V0JPJZVZqPqzVvzQtt"uSuSu"SRQ"RPP"P01t13t38u0RRWu6W68uGRupRSupSup4S48up: V5VRRPRRP-R@AtACtCIu@bbWu W  u IW@u <u <IZ+Q+/P<IQfV V IVPQtQStS_uPssSu_S`atactcu`mmQuoRttuQuRtt# ufPfkukPSP S P S # P# # u^^ku k# Wku` u`hVk V)7W?iWkW W0 1 t1 3 t3 u0 = = M PM Q uQ P u t t 8u  V ) u) _ V_ 8u ) u@Z 8u@ C S S S  S  S$ ) Sa ESS>CSxS*SXSjoSSSSES 1 u@1 C u@C z Sz u@ D u@D S u@ ) u@_ fu@fSu@SVDSD\u@\Vu@Swu@wSCu@CxVxu@u@*u@*XSXu@VPu@Pu@EVEu@8V25P5jSS ) ul_ 8ul ) uh_ 8uh C S SPPES8S ) ud_ 8ud ) u`_ 8u` ) us_ #us#3P ) ur_ 0ur0@PE8ur u@ u@ ) u@_ a u@a u@ u@ u@ Eu@Eu@u@xu@xu@u@*u@*Xu@Xu@Eu@Eu@8u@ S0 _ SttuttuAAEuERRououEu E33NWNRuRW EuE 33KVKRuRVR"RttuoqPLSSS} V <R<bVb|R|:VV1TVVP4bPPKRR2bR R#RRJu{JWRRWu{u{Su{SQWu{SSSUWtt uP3V: V(6Wd_ W W(-PPP(0S S S(:ug ug t t u P t t u   uW V VW S SL W W t t u   V u V@ M PY c P~ PY S t t u  uo _S_bPbeSPPPPmPP=OSORPSPlup.=upOupupP.9SOSS u~.=u~Ju~u~u ul.ululu uh.uhuhttuuKKVu VttuSSttuuuu<<bSbzu zSuu <<uu8:PemSmsVszPzVSP;V;EPEuVmvWuW t tuPttuttguPPXgPR#RXgRVS4XRpqtqutuupttuHWJWtt uttu$$VuVttuPulRuhS>V>}W}VWttu>V>EuEVAWAEu EW;SESttuttutt0u01t13t3u0LLrSruuuSegRnuRttuttu66u6S,S(SSSR~S6mSmuXS&uX&,S,uXSuXS]uX]S1uX1RSR~uX~S6;V68PR~VP~Pttmu-V-/u/mVpqtqstsupPWPWPWpRuhpQudS SululupupVVtt u u} u}EQpQ$ m R R0 R QERHR Q Q Q - Q RWPWWW^P^ W  P W P  W & P& W P  W  P & W& - P- @ WB W WTu}T u} u}  u} u} S u}  u} - u} S u} u}*S S- > SB S S - S S V ? V V V u} u}  t  t [uZ V 9Vc 9u}w S2 S Sc W 9W`atactc[u`atactcuttuttEuu//Eu00WPWu /Wu//Eu uE uE\ V*EVPQtQStSTuPffWuWuWTuPssuuUUTuPf fVu!V!TukmQmRQRQR;QUR RMROTR`atactcu`{{u`u u WWWW~WS9S:`SS:S`~SR>jSjQSQR Q R4S4jRoR5R:[R`yR~Sugu~uu<?u?~uVJVV~VPjPPPtt7uP7uSu S/u /7SWu'W'/u/7W Vu$V$/u/7V@AtACtC u@YY ud@__ u`@t t u\@ee uX@nn uT@yy uPWVuHuH } uH} V R  W  V uH V 3 R R VuLuHuLuHuL uH B uLB uD uL  uH uLkSG S SSG S Sttuttu !t!#t#mu 66mR 66mQpqtqstsupSuSpWu WpVuVttuWWurSQOQS,SJVCVtPPRWRttuSuSu WuWPVPVVRR !t!#t#|u **QSQSuSqSqsus|S3GRS|Rttuuu u VWuttuttu !t!#t#%u01t13t35u@AtACtCEuPQtQUtUWu`atactcu`u`u `;u;=QuR.R.TRTfRR SStt~uttuuWSVttEu1V18u8EVP8ESPQtQStSuPddSuttuVVSSWWtt uuDuDZuZyyuu  u SDu\Zu\yu\u\ VVDVWVyV SDSVSySSDuLZuLnuLuLuL  uL  t  t Vu  S uS  S uS  u 99PuPV D D [ u [ s s Vu  5 5 t Rt [ u[ R & u& 7 R7 S uS ^ R^ u R jujzRzVu [ u@ S u@ u@9Pu@ [ uT & uT| S uT uT(PuTbjuTuTuTuT.<uTNVuT [ uD S uD uD9PuDQ S S S S S [ uX S uX uX9PuX P P P`atactcuttuVWPSPStt^u`atactcuttuttGu--BuBG9P9Gu GQPQtQStSuPccWuPccVu PccSuttueP|PPPPPP4=PZcPPPPPRjSjsRsSRSRSRSRSRS(R(kSktRtSRSRSRSRSRS !t!%t%u OOu AAu Q9JQttKuPVV KVKuKu RSRSRdSS KSQ0Q[QQ >QRR[RRPQtQStSvuPnPnvulPnRnWREWGtWPnQn{SSSGYSusus?RQ?QRRtt uPWWW  WRVRV V VQSS0 S SP uhP ] uhR]  P] ud] t udRt  P u\ u\R  P uX uXR PR uXR  uXus\ us  usur  uruq4 uq uq R  uq upV V  VS0 S  S  t  t W u 4 4 W u u W k R Rm  R ! Rm s R RgRg|R"REyRRRpRuR&RQZRRcRR[RR5MR>RRRJiR k Qu  Q R! c Rm s R+BRZmQ~QU Q Qm  Q  Qm Q Q QgQgQQ"QEHQcfQvQQQ Q&QQZQY m Su { S SU SSlSSS;ESYcSkvS~SSSSSS0QSS]bSSSS1`SS.8St~SSSlvSS-2SISSt~SSSdiSSSzSSSSSpzS.8Sv{Sh  u 7uFRu_kuxuu uYuiW uS  u W u . SU p Sp s Ps S P Y Sm u S S P U S SlSS;SEYSckSv~SSSS'SS0SQS8SbSSS1S~SS5SSv}S~SdSS(Se G Wm } W W *WWW W_W|WbnWWWWv~WWG m Wu } W~WI e W WI V Vm | V V V V$VVVETVc#VV+ZVV R R|RbRRu  ul ulm ul1ul~ul>vuliululUulPRW Pu  uh uhm uh8uh~uh>vuhiuhuhUuhuhRW Pu  ud udm udFud~ud>vudiududUududRW ud RQ3R8<R<MPRM\P{RPRPRPRPRPRP5 9 R9 < PR< ? P Q3Q8JQJMQRMeQ{QQRQQR Q  QR < Q< W QRu | V Vm V _V{W Vu  u um uu~u>vuiuuUW u Q[Q>Q SwS-pSz.S8vS{W S QQ3Q8MQMyQ>AQu  u` u`m u`u`Mbu`u`u`~u`iu`u`Uu`u  u\ u\m u\u\Ebu\u\u\~u\iu\u\Uu\u z Pm PPP S Rm s RSRXPRSRSRS1S1[R`bRbdSdkPkSSPRRS-P-5RMRSRP~RJRR.R.7S79R9USu z Pm PPPDrPu  uXm uXbuXyuXu  uTm uTbuTyuTu  usm ususNbusyusu  udm ududrbudyud>IuhI[Ruh>EulE[Q}ulu  um ubuuu Rx}R}SRRS2ISINR PPvPPm  QQm  RPR m Wu } W_|Wtt uttu$$SuttuPupRVVPSttvu**.S.0u0tStvutt<uR%:R!S%<S"V%<V#W%<W@AtACtC1u@u1u@oo1u @77DuDuu$$1uPup1upPRWWDWW$Wu`1u`SRcRx{RRtSDSS#S8S$S}(ul(*QDulQulQ$ul}P&YPxPP@AtACtCu@**eueoouuRuR@ffu u @XXRuRuRRuR~R~u@ eueo ou u RuR @XXQuQuQRuR~Q~uwudQuduhQuhRuhUZuhP ululRulP#R*mWoWWRW*_S_eupekSkoupoSSRup\S^PPejPPPRlPttuttAuttuPPuPPu uu u u PPuPPWRWuVQV+Q+0V0QVQPQWWW W>SSS "SouSWQQ&PoPPSS"oSuSttuWuSVtt=uSuS$u$=Su $u $qqPu Paau u =$u$BuBaauu= u $u$ BuBa au u= PJXPP58P V P-HR`V$VqVPPVaV=VRqRBPR@AtACtCu@ZZ@V@CuCVlxPzP->Pu~PPP5 : P B RRlCuuu u | u u5 u  u- +uulAWu W / W W W W`WdWP Ph y P PPQ 0 Q0 E PE t QPPl?Su S| S = SY SS`Sl?Su S| = SY SSS`SZ`P3>PCPPRuPtt.u  P.u01t13t3FuPQtQStStuP^^tRttuWuWuuVVPtt}uIuIPP}u[[cu cDu DOO}u DuDOOu}u# #DuDO Ou u0 0}uzuz}zuz}Ycu}Du}du}u}0}u}Y`V)S)VOuSuV'>SzVV[}VYcu}Du}O}u}Ycu~PDu~ju~u~0}u~>SjuS[_SDSjuSzSS0}SR3DRjRRzRRR[sRYaW4DWj-WzWWW0[WY`VDVjVzVV06V[}Vtty uy u`y u\ S u  S J uJ y Sy uXy uTy uPy uL y uH$y uD ud J ud uh J uh ul J ul up J up S J SPQQ8 Q Q / QV V V J V u J uE u@E [ P[ u@ J u@ P P t t u S t t u  u  u  r Sr  u  I SI u Sj r P PY r P Pr { W : Q: P WP i Qi W Qr x V L RL V R L VR R V t t u " " ? u? F F u  u  u (u(??u  u uV Vu 6u6@ @u u (u(??u  u  u uuuu uLuuu SP]S/PPNSPPVSS V(V?V VVgVVSuVV%S%V+V6@VV uu5RR uuuuP uuuu uuuu]SSS SS#6S@USSSS WWWW@WBW=WeWW uuuuPP{PPWYPSUSSS-RU`RAMPQR6@R !t!#t#u .P.SSttutt/u~~/u~S-TSSI Su | Sh!!S"'"S#L$S&&S(A)S*+S++Se,x,S,W-S~uLSuHuLRSRuH-S-uH$uL$SyuHyuLuH uL Q uHQ ^ uL^ u uHu uL !uH!9!S9!!uH!"S"L$uHL$]$uL]$$S$$uH$%uL%%uH% &S &y&uHy&&S&Y'uHY''S''uH''uL''S'8(uL8((W(0)uH0)])uL]))S))uL)=*uH=*n*Sn**uH**uL*b+uHb++S+ ,uH ,?,S?,e,uHe,,uL,$.uH$.Y.SY..uH.&/S&/U/uHU/\/S\//uL]u$W$u\ W\ u uu W L$uL$]$W]$$u$%W%e,ue,,W,-u--W-/u~ u /u~0u0YSYuS^ u^ u Su !u !!S!!u!!S!I%uI%L%SL%T&uT&y&Sy&'u'6'S6')u))S)*u*=*S=*+u++S+/u~uPS`%uP`%%S%/uP~uw#u#*&u=&/uXQQUlPsPPP9!M!P~WWWXWW !!WL"#WA%C%P*&=&W6'Y'W$SQ Z S S$%S~$ul$$Q$/ul~V.,e,V~VW..Vtt utt`u**.S.0u0^S^`u`atactcu`u`Wu WwSStt<uu<u @S@JuJ<S]]<u;;Ju$J44u$<;Jud^ud9JQVQ<Q9;Vr.V<V9JRYRRR%*R5:R9JupuhuhRuhupR up  R upuhupuhup%R(*up*-R-5uh8:uh:<R;Jululul;CV,.P@AtACtCuttGu>V>@u@EVEGuPQtQStS:un7VqQ:QqQ%:QqS%6S@AtACtCu@]u]u[WYSZVQQG]Qtt2uP2uhR2RS.S@AtACtCu@ePeu`@oRouh@Qud@u@u 55Nu Nu ZZdu d~~u u RVVVVhS`SzSSS}uoPPuoPRR 5R@RCRdRRS@NSSmSSttu%P-3P9tPP5PD_PzPPPRupQSQ)S)-Q-SQ*S*5Q5zSzQSQS*V5HVyVVV%zVV%P9tPPP%P9tPyPP%PPPP%P9tP5_PszPtt'u*P*'ug6R6'ug=@PPPPPP  W^WWWWiW<HWW  SO S XVXqSqVViV<VS V ugxugugKugi ugq V uh V T uhT F VF uh V W  uh , V, F WF FuhFKWKeuhe{V{uhV'uh uh uh uh> 'uh  S S S> I S S& p S PV{S&SS 'S uh uh uh> 'uh uh uh uh> 'uh W  S  W & W+  S R > S p S S$ , P PFVSSPPPPPPKiS  V V? V V> F V k Vp V:FV&VVVV & Sp v S FS|VV  SS=SSiSS<RSSpyPyRPP RPRP RP$R upupKup<up ululKul<ul uhuhKuh<uh ududKud<ud  S!ZSPKSS  S=SSVS<RS01t13t3ujudjuhjSWSSjWWjuljVVjSStt@uQVQYuYVu\V\cucVu@VNSNYu YSu RSRcu cSu @Sr{PP/GWPV_WcW@W@AtACtCu@QPQVP^SttuPuLRuH0u0{S{uSuSuSYuYbSbuupWupYWYupPPPbStt#uP#upR#uoQ#uoZ SZ u S !u!C!SC!G!uG!!S!"u"E"SE"#u? U P P W !W!E!WG!S!W_!!W!#W #!#t!###t## 8u ### 8uz$$S &3&S'(S>+d+S##u~##u}#,$S,$H$u~H$t$u}t$z$Sz$$u}$ &u~ &L&u}L&'u~'(u}(Z)u~Z),*u},*>+u~>+s+u}s++u~+ 8u}$ %P=%N%Pb%%S%%W&'Ws++W#$u~$$S$ 2u~ 282S82 8u~$%u~%H%VH%%u~% &VL&'u~s++u~&&V#!$V$%V7%%VL&&V&&V/)Z)V++V082Vs22V22V#3-u~3-|-S|--u~-/S/50u~50n0Sn02u~22S2?3u~?3l3Sl33u~33S3+4u~+4x4Sx45u~5\6S\66u~6 8S#,u~,,u~,s/u~s/~/u~~//u~//u~/0u~050u~501u~182u~82s2u~s22u~2 8u~ttu !t!#t#7u@AtACtCEuPQtQStSuPuwuwuPkkzQzuOQOwuwQgRSRSV3VkVzWWwWWzupVup.up9kVkwupVzPJQPSrPttuPulRuhQYudYwQwud,,PuPlluHuHJJYuY||u*Yup|up ,SP[SHNSY|S,WPW|W,NS|SttuSuSuu RPttuGGuGGu GGuG GuGGuGGutt uu u u   u R R } u}  uPPP~P  PI N P PSS~SS  SIVVV~VV^VVu V * VG Q V V V VWW~ W W p W W- WP3P PSS~SGSGLPLoSoPS  S SIVVsV~VV^VVVu V * VG Q V Vp t V V - VWUWU]P~W W W P W  P- WSSNS Sp { S{ P  SRqRR R~RR^lRRR1rRRR RM Q RS { R R R R  RJ L PSS~SS  S<qPS9S~S  SSS~SSN p SQQQQRqRR R~RR^lRRR1rRRR RM Q RS { R R R RSS~S ^S  SRqRR R~RR^lRRR1rRRR RM Q RS { R R` R R R R R RSS S SIVVV~VV^VVVu V * VG Q V` c V V V V V VSS~SS  S G S S t t u  S Vu VttuP R uP1Q1uLQuLu$$IuI+QVQQQ$7Q RR<V<KRKNRVVVRFVRV-iRVRRR$7RuTVuTIuTuT#uXuX$IuXuXCtQQI`QQu\Iu\~u\u\-RRR$RmRttuuBBcu cpWpu Wu (W(u Wu W>u >JWJcu cWu >W>cu cWxSP}P(nSS ScPS+ZPZoSP6KSzPPJcSSPPSPSSNcScPSSS.CS]rS~SxR5zR(5RR>JRRxusus(us~usJcususcusl}usrusxurur(ur~urJcururcurv}urruru{Pu{Pu{xu{=u{Jcu{ttuttuttu !t!#t#Bu 1P1BuX qR2BRRsPsQVQVQVQ/V2BPxW0WPQtQStS/ uP}P}/ unP}R}/ unP}Q}/ unw9SSVnVU ` V% / VSzSU ` S% / S unyunWunW3un39un9hShoWoSPSPSPS#un#*W*XSXZPZ^S^{P{SWS,W,zSzunP"Q"&P&BQBrRrQWunPSWU SU ` P` S % un% ' Q' / RZQ Q! 5 QV@VARV R! 5 R< B V` y V % VVnV< Vy VP PPJSSS8wSS6<SeSSWWGVV VV8uVV6YVeVJSSS8wSS6<SeSS:hRhQRQ+Q8Q[Q-TQewQQQ QGV VV +V8uV-YVeVRQR Q+8Q8dRdQ[Q6TRYwQQR Q VV+uV6VPPR0 1 t1 3 t3 u0 ; P; U SW S t t u  W u W u  V u V u P S  t  t u Q Q u V  VuVVKVV.5V8RVGV-V GuLuLuL-uL S S  S1 SuSSSS  S}S.4SS:<S R  Q $QRRQ(RPRvRvRRR:CRiRRR SS Gululul-ul Gupupup-up+Q:CQ Gululul-ul Gupupup-up V  VuVVKVV5V8VVV Gupupup-up  SSR|S Gu`u`u`-u` uLGSuLuLS-uLSuLS2R S SSKSS.4SSSPPQPQR#QPQR S SSKSS.4S-SSQ  SL SKSS.4SStt9u7V79u6S69u P/6P@AtAEtEu@UUVu@UUSu C]PoPttuPuPuVu Sutt uW uV u tt4u@AtACtCuvVV{SS,<PgjPPP !t!#t#quttuttuttUuPUuUu KS`atactcu`nnSu`nnsPsu `uttuQPu ttuPPu tt%u%QRu R%u R%R01t13t3u0TTup0ppVu Vu V0ppWuWuW0\ \ul0eeuhkuPPPuPP,N^^NVYK  J K xv[qtyXkmt  ; F`p$ps $Q@{`$ } ${``g{jvXb7H48L8L 0UZlP)e &q { &q { 33xemKUHcxemKUHIjahVQT8icf[^MHicf[^h|6H}6]}6]+H0HX`vxzI8 T K y x h8 T q y h (o y K h(go y  8  !  !   8 H M [ S U `U=UzX =Uz,X0c_a9H=U04r=UBHz8Xc_az8XXz=Xz=  4>4>(())i`zBi`zB5:XJM* x  _ 8 ) C Q 8ozuxPz `C*oej"RT\<D&U_ E< - 7  z `)09]8EozuxCxT\<D&]f]fh EEX*xv ~  Uh*uQgA;E-  U7U(- (- 2@  @2kyWelW_ @ o e m = qs|P]b`t`tR~ 1~h0C0 H w L @O T 0Ed0E@X$7HJ~VHJ~Vj|@Qp:Bs{^fMQ<@+/ z r z ] e H P 7 ; & *    R x = ? N R B D {}Xczoxeil>l/pB u ~ h|+ h|+$8UW i>v~=$8{W UYU-i>v~=Ui~5-d`8hUW_J~B-d` A>JBE;=>%>  v~2~v(<(< (< @GRXWZ8xZpE 8 Zp | j m d E {  8 |  W  | `  #    - Y[#H0}/0}13 P  @ P / 8 @ P B ! W p K O 2 ? p r p r    5UAg=RQ6@--+:+**)@*'Y'*&&%%L"#!! h!` u --+:+**)@*))'Y'L&&*&=&%%%%%%C%X%?%A%'%/%%%f##"^#""L""!!!h! -"~fnQYAI05(.) ***?%A%%%%%%%C%X%($%L$]$u q$$&'%*&--&&-$.'())/// /EHT*,HT.E3?E6<V] 'Ki ( @ ? e V] 'Ki(  p Y F @ ? o _ d W  e z $=Eq <OReOReyH<K=N`=N`opjW P`@h H!Z!_!e!m!|!","m!|!","|!!!!##$&$%@%% &&&&&&&1(/)**_(p(v({()**+)* **H,}0?3 802H,}0?3 82282s201H,,7 87777s7w7_7c77O76766666666R6656=65)65544444474x43+433333333n3v3i3l3K3_32282s2010000f0}0A0I0>/50 /(/t./-h.------|--N-S-E-H---,-|....\/m/s/x/Y3_3i3l3333309HP   o m  o m~  o m~m o om~ - p K - q t h~- ] p G N L H J [ * ] p G N L H J [ * ] p G N N ] N ] ^ N N ^  N   N   G x   G x X`$>Xc8B?-6 0?-6 0Ye08Ye08bh% / U ` zq w -8H0P } q w -8H0P&    } q t SiR0Pb  } q t `iR0C"P:  b r } :xq t `iC"P } R0 -SiH@Sv*H~*H@S ACQ}HJEGACQ}HJEGvx}uKQ\bTVerTVerrrX\^^.symtab.strtab.shstrtab.interp.note.ABI-tag.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rel.dyn.rel.plt.init.text.fini.rodata.eh_frame.ctors.dtors.jcr.dynamic.got.got.plt.data.bss.comment.debug_aranges.debug_pubnames.debug_info.debug_abbrev.debug_line.debug_frame.debug_str.debug_loc.debug_ranges#(( 5HHP1o0; ȄC؋ KoXoĐg  p ԑX y,,0t\\  ^^8z YY$$Y,,Y00Y  Z$$Z[ \ \`pdz3GcKb |-0,#>8j` Cv.Q<0"% l (HȄ؋Đ ԑ , \  ^^$,0 $ !",$:,GP ]lz  ,^ @f : `  (-9 HOXbn$x }`~ y 0w W  ( \q #,50A,G$ W  a8 s ~"2B1 8#40LBDO@`<l7 x:% P HD@D X`  7D Ph\PF lI |7pxpR Sm *Y[ <lQYfr0~  &  #> x Qex\ d  "r 2 N" a |_ ` p i x  #% ?' V1 s|@" B JW  X   `Z  # $9 J ] 0f <x   m $  < @|" pc 1  `, p< h R le K  u \ 1  e  } p Р T d   3 m F ^ V ] Du ) t  d  g ;   @  `E  @ % 0  . `oc = @V G 9 P T _ \ s $   -   oU \   V9 ( P < M <^ ^e ~x /  X Z   pkq  ? 7Lg-l= L^Rdw"p z  %  Q \PuF ` lq0E\ QXc`'U ,Q ( ; Iq7 )  @PYB j pV J 5  2 @`* )# 9^H8`}7 h sFA @P @ie d0^ Ps ) =Jl _и y :  4 2:7 8<2`[ E OUDr0 P\M  D  h} 4I # /L :LC _ pFT` ` &@   2 L 'n < m Q/ ` o4? p0 P  L  i\ xPI "`\q 2E ?Lp_z@}A Pl  $ %( ^Z 6  ( f1|^ ?u P`l p|L k\  c5hM  ] d ! 4H|W. owPr @ P, eq!  *W4 9EW[U lM }%  m IJ p|b Pf 4 <@ 08@S c ap pp+ y ZQ  0pO @m4 P m 7lEz\p[u j}#  @ l&% 7#LW: ZLiz81 $!`W9 h wI!& )y ;@G FXDbHi4yp G 1B 0P 8pA 4 }E   #0m 5f ClWxe xi CWC $ UA t@Km  <A@ P`@A \\5 h\5 ntp z^ УC t@r , @L 0<   init.cinitfini.ccrtstuff.c__CTOR_LIST____DTOR_LIST____JCR_LIST____do_global_dtors_auxcompleted.5706dtor_idx.5708frame_dummy__CTOR_END____FRAME_END____JCR_END____do_global_ctors_auxaprx.cdone_once.6777syslog_facssig_childsig_handlertimetick_countold_tickttyreader.cttycountttyspoll_millispoll_millis_tvax25.caprsis.caprsis_upaprsis_downAIShcountAIShAprsISdefault_passcodeaprsis_comssockreadaprsis_runthreadaprsis_closeaprsis_queue_aprsis_reconnectAIShindexaprsis_readup__PRETTY_FUNCTION__.6865buflen.6758buf.6757beacon.cbsets_countbsetsfix_beacon_timebeacon_itbeacon_resettimerbeacon_setconfig.c__PRETTY_FUNCTION__.6589__PRETTY_FUNCTION__.6575__PRETTY_FUNCTION__.6549__PRETTY_FUNCTION__.6486netax25.ctx_socketscan_linux_devicesnetax25_devcountnetax25_devsax25ttyportscountax25ttyportsax25rxportscountax25rxportsrxsock_readdiscard_read_fdnext_scantimerx_socketax25ttyfds__PRETTY_FUNCTION__.7144erlang.cerlang_timer_initerlang_time_end_1minerlang_time_end_10minerlang_time_ival_1minerlang_time_ival_10minerlang_backingstore_growerlang_mmaperlang_time_enderlang_findlineaprxpolls.c__PRETTY_FUNCTION__.6495telemetry.crftelemetrycountrftelemetrytelemetry_timetelemetry_labeltimerf_telemetrytelemetry_datatxtelemetry_seqtelemetry_paramstelemetry_labeltxtelemetry_labelindexigate.ccellmalloc.chistorydb.chistorydb_cellsnext_cleanup_time_dbs_count_dbskeyhash.cparse_aprs.cget_symbol_from_dstcall_twocharpbuf_fill_posparse_aprs_compressedparse_aprs_uncompresseddupecheck.cdupecheck_cleanup_nexttimedupecheck_db_allocdupecheck_cellgaugedupecheckers_countdupecheckerskiss.cinterface.ctoaprsinterface_storeinterface_default_aliasespbuf.cpbuf_cellsdigipeater.cdigi_countdigisrun_tokenbucket_timerstokenbucket_timermatch_tracewidecount_single_tnc2_tracewidetry_reject_filtersdigipeater_receive_backendfree_tracewidedigipeater_config_tracewideregex_filter_adddefault_wide_paramdefault_trace_paramtracewordstracewordlenswidewordswidewordlensvalgrind.cfilter.cfilter_match_on_callsignsetmaidenhead_km_distancefilter_parse_one_callsignsetdprsgw.cdprsgw_ratelimitdprsgw_nmea_igategps2aprsSymsdprsgw_flushcrc.cagwpesocket.cnetresolver.cnetresolv_die_nowresolve_allnrcountnrnetresolv_runthreadtimercmp.c_GLOBAL_OFFSET_TABLE___init_array_end__init_array_start_DYNAMICfileno@@GLIBC_2.0data_starthistorydb_hashmatchaprsis_set_heartbeat_timeout__errno_location@@GLIBC_2.0sigemptyset@@GLIBC_2.0historydb_insertverblogsprintf@@GLIBC_2.0verboutsrand@@GLIBC_2.0open@@GLIBC_2.0clientptr_to_cellheadconnect@@GLIBC_2.0dprsgw_pulldprsmmap@@GLIBC_2.0getpid@@GLIBC_2.0filter_cellsizepthread_join@@GLIBC_2.0strerror@@GLIBC_2.0keyhash_initdupecheck_cellsdupecheck_pbuferlanglog1minigate_to_aprsisconfigline_is_commenthistorydb_keymatch__libc_csu_finitocallfreeaddrinfo@@GLIBC_2.0syslog@@GLIBC_2.0all_interfaces_startaprsis_startswversionrandom@@GLIBC_2.0setlinebuf@@GLIBC_2.0beacon_childexitswnamedprsgw_postpollparse_ax25addraprsis_add_serverpbuf_gethistorydb_dumphexdumpfptimetickttyreader_getctv_timeradd_secondsfilter_preprocess_dupefiltersqrtf@@GLIBC_2.0signal@@GLIBC_2.0interface_receive_3rdpartymyloc_latstrhistorydb_allocbeacon_config__gmon_start___Jv_RegisterClassescalc_crc_16ttyreader_linewritefilter_cellalignduperecord_align_fp_hwrealloc@@GLIBC_2.0netax25_initvalidate_degmin_inputErlangHeadaprx_cfmakerawtickreadconfigcellmallocmanycrc16_tablestrtod@@GLIBC_2.0socketpair@@GLIBC_2.0historydb_cellsizestrchr@@GLIBC_2.0recv@@GLIBC_2.0interface_init_finicalloc@@GLIBC_2.0strncpy@@GLIBC_2.0putchar@@GLIBC_2.0write@@GLIBC_2.0kiss_pullkissbeacon_postpollfind_interface_by_callsignerlangsyslogaprsis_loginhistorydb_insert_ErlangLinessendmsg@@GLIBC_2.0fgets@@GLIBC_2.0regexec@@GLIBC_2.3.4igate_startmemset@@GLIBC_2.0filter_postprocess_dupefiltersetsid@@GLIBC_2.0telemetry_prepollmyloc_latfilter_process__libc_start_main@@GLIBC_2.0netax25_prepollexecl@@GLIBC_2.0aprxlogfilehistorydb_insert_heardrflogfileinterface_receive_ax25__assert_fail@@GLIBC_2.0netax25_sendax25pthread_attr_setstacksize@@GLIBC_2.1kissencoderread@@GLIBC_2.0erlangoutconfig_SKIPSPACEtime_reseterlang_addfilter_parsegmtime_r@@GLIBC_2.0filter_cellsmyloc_lonstrigate_from_aprsisconfig_SKIPTEXT_IO_stdin_usedgettimeofday@@GLIBC_2.0keyhashpbuf_allocstrtol@@GLIBC_2.0dupecheck_initpbuf_putcellfreemanyfree@@GLIBC_2.0erlang_data_is_nonsharedtnc2_verify_callsign_formatopenlog@@GLIBC_2.0historydb_cellaligndigipeater_prepolldigipeater_postpoll__data_starthistorydb_nointerestinterface_is_telemetrableax25_format_to_tncnetax25_postpollfflush@@GLIBC_2.0aprsis_set_loginregcomp@@GLIBC_2.0tcflush@@GLIBC_2.0ioctl@@GLIBC_2.0socket@@GLIBC_2.0dup2@@GLIBC_2.0__ctype_b_loc@@GLIBC_2.3telemetry_postpollkiss_polldebugpthread_attr_init@@GLIBC_2.1beacon_prepolltv_timeradd_millisgetaddrinfo@@GLIBC_2.0atan2f@@GLIBC_2.0nowerlang_initfclose@@GLIBC_2.1ErlangLinesCountkiss_kisswritedup@@GLIBC_2.0getopt@@GLIBC_2.0filter_lat2raderlang_setstderr@@GLIBC_2.0fd_nonblockingmodeinterface_configmemcpy@@GLIBC_2.0cfsetospeed@@GLIBC_2.0ax25_to_tnc2printtimestrlen@@GLIBC_2.0cosf@@GLIBC_2.0aprsis_configvalidate_callsign_inputfopen@@GLIBC_2.1aprsis_threadaprxpolls_resethistorydb_dump_entryhistorydb_dataupdatenetax25_sendtoreadconfiglineunlink@@GLIBC_2.0ttyreader_registerratelimitmaxttyreader_parse_nullparamsdigipeater_receiveaprxpolls_freegai_strerror@@GLIBC_2.1timecmpall_interfaces_counterlang_prepolltelemetry_starterlang_start__dso_handlewaitpid@@GLIBC_2.0pthread_sigmask@@GLIBC_2.0historydb_initcellinitfilter_parse_one_sfeof@@GLIBC_2.0strcpy@@GLIBC_2.0__DTOR_END____libc_csu_initprintf@@GLIBC_2.0process_message_to_myselfax25_to_tnc2_fmtaddresstocall25historydb_newaprx_syslog_initcrc_ccitt_tablehistorydb_nopostop_interfaces_grouperlang_backingstoreaprxpolls_milliscellmallocconfig_parse_booleanstrcasecmp@@GLIBC_2.0clock_gettime@@GLIBC_2.2cellfreetv_timerboundsaprsis_postpollttyreader_postpollregerror@@GLIBC_2.0pbufcell_aligninterface_is_beaconablepidfileaprsis_queuenetax25_addrxportaprsis_prepollopenpty@@GLIBC_2.0log_aprsisclose@@GLIBC_2.0fwrite@@GLIBC_2.0fprintf@@GLIBC_2.0time@@GLIBC_2.0setvbuf@@GLIBC_2.0ttyreader_newnetresolv_stop__bss_startmalloc@@GLIBC_2.0tv_timerdelta_millistelemetry_configconfig_parse_intervalmyloc_coslatinterface_transmit_ax25erlang_postpollhistorydb_atendparse_aprs_messageaprsis_set_filternetax25_openfputc@@GLIBC_2.0hist_lookup_intervalduperecord_sizememrchr@@GLIBC_2.2dupecheck_newttyreader_inithistorydb_postpollstrtok@@GLIBC_2.0scan_intnetresolv_startpoll@@GLIBC_2.0pthread_create@@GLIBC_2.1historydb_freehistorydb_lookupconfig_STRUPPERsigaddset@@GLIBC_2.0interface_transmit_beaconerlanglogfilestrncasecmp@@GLIBC_2.0netresolv_addmemmove@@GLIBC_2.0filter_lon2raddie_nowlastposition_storetimettyreader_prepollsend@@GLIBC_2.0_endstdout@@GLIBC_2.0netresolv_threadsinf@@GLIBC_2.0puts@@GLIBC_2.0fork@@GLIBC_2.0digipeater_configtcsetattr@@GLIBC_2.0calc_crc_flexaprsis_loginidfcntl@@GLIBC_2.0flock@@GLIBC_2.0rand@@GLIBC_2.0sscanf@@GLIBC_2.0pthr_attrsfscanf@@GLIBC_2.0calc_crc_ccittmemchr@@GLIBC_2.0cellhead_to_clientptrvfprintf@@GLIBC_2.0dprslogfileaprxlogdupecheck_prepollparse_aprsoptarg@@GLIBC_2.0myloc_lonmycallpipe@@GLIBC_2.0aprsis_initcloselog@@GLIBC_2.0crc_flex_tabledigipeater_find_dupecheckfilter_freeconfig_STRLOWER_edatakill@@GLIBC_2.0pbuf_initaprsis_stopkeyhashucdupecheck_gethistorydb_prepolldupecheck_putrecvfrom@@GLIBC_2.0pbufcell_sizedupecheck_postpollnew_cellblockstrcmp@@GLIBC_2.0check_crc_16find_interface_by_index__strdup@@GLIBC_2.0exit@@GLIBC_2.0netax25_startcfsetispeed@@GLIBC_2.0dprslogaprsis_interfacepthread_setcancelstate@@GLIBC_2.0dprsgw_prepollfilter_inittv_timercmprflogpthread_cancel@@GLIBC_2.0ttyreader_serialcfg__i686.get_pc_thunk.bxdupecheck_aprsrateincrementmaxmain_initaprxpolls_newpbuf_newttyreader_parse_ttyparamsaprx-2.08.svn593/debian/aprx/usr/sbin/aprx-stat0000755000175000017500000007240212313357163020277 0ustar colincolinELF4L`4 (&#444`````l ttt((( Qtd/lib/ld-linux.so.2GNU    "K9o11)  ~}Pw26q!C`llibrt.so.1__gmon_start___Jv_RegisterClasseslibutil.so.1libm.so.6libpthread.so.0libc.so.6_IO_stdin_usedsprintffopenstrncpyputstimereallocgetpid__assert_failsyslogfclosestderrfwritefprintfstrcmp__libc_start_mainfreeGLIBC_2.1GLIBC_2.0Vii ii dtx|     US[ !tN X[5l%p%th%xh%|h%h%h %h(%h0%h8p%h@`%hHP%hP@%hX0%h` %hh%hp%hx%h1^PTRhhQVhP/US=u@l-hX9sBh9r[]Ív'Uptt $pÐ \USȉ)kpollsaprxpolls_newSystem time has gone too much forwards, Resetting timer '%s'. dt=%d margin=%d System time has gone too much backwards, Resetting timer '%s'. dt=%d margin=%d /<FV P LHo\   hȄooo^tƅօ&6FVfv??GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2GCC: (Debian 4.3.2-1.1) 4.3.2$P"LX`{ 0d$~|d!u_IO_stdin_used3X# erlang_initeerlang_start3erlang_postpollgerlang_prepoll erlang_addy erlang_set! aprsis_thread3 pthr_attrs erlanglogfile erlangsyslog erlanglog1min erlang_backingstore ErlangHead ErlangLines ErlangLinesCount erlang_data_is_nonshared{ =printtimesaprx_syslog_initmainaprsis_threadpthr_attrsmycall*time_reset<debugNerlangout`aprxlogfilerepochtime}aprxpolls_reset*aprxpolls_freeRaprxpolls_newaprxpolls_millisaprsis_threadpthr_attrseFtv_timerdelta_millistv_timeradd_millistv_timeradd_seconds5timecmpbtv_timercmptv_timerboundsaprsis_thread!pthr_attrs3nowEtick ĆĆ\ointylOK'/build/buildd-glibc_2.7-18lenny7-i386-lqGp8g/glibc-2.7/build-tree/i386-libc/csu/crti.S/build/buildd-glibc_2.7-18lenny7-i386-lqGp8g/glibc-2.7/build-tree/glibc-2.7/csuGNU AS 2.18.0 ]`/0\oint*8a]oZp612- Z# # # # # :# # # # 9#$ &#(  #, "#0 $#4 &Z#8 t*Z#< ,z#@ 0>#D 61L#F 2#G n6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l '  # -# Z#    ' drM|FO G# CH# 2E$ 6y 7y 8  #K 9Z   )fd *Z# d +S# c ,S# 1739 uE = E#  Z#  Z#  &#  gd K   #  #  #  #  #  # & #   # y Z#  # m #' ! #(  Z#,  s#0  s#L E Z#h C Z#le10 #p   s bL C #  Z#  Z#$ ! #(  #,  #0 " C#4  #D S ;} h<# 3=}#L  GZ(ZYAp`o, ?  OZLjk_!(NZ!YNZ"EFP_"iQZSvЈg(Z;# $%8npO&iZu{'{'D&fpu{(P)EB-{ -*+)Z ja,appa+Zp-appa.2-' P0S21Zr)i3Z)E4-/G_0e@%0@%vy P-erlg:ZZZ )E-p / P2Z1 &1Ħ1&1;Ȧ14223 ,O3R - 2" 2 &2k Z2~ Z2 Z3`,3 )Z3w*Z3-36 37  -3H8Z3K9Z0\0\oint*8a]o2-i Z# # # # # :# # # # 9#$ &#(  #, "#0 $#4 &Z#8 t*Z#< ,z#@ 0>#D 61L#F 2#G n6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l '  # -# Z#p  i  '2E$678  #K9305sbufZ@EpZPZ0,R- "XkZP~ZTZLHZDW930\oint*8a]op2- Z# # # # # :# # # # 9#$ &#(  #, "#0 $#4 &Z#8 t*Z#< ,z#@ 0>#D 61L#F 2#G n6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l '  # -# Z#    '|F G# CH#2E$6B7B8 R #K9#)fd*Z# d+S# c,S#3  9 u = #  Z#  Z#  # ]bU$app$S0ЏRapp0$EЏoapp$p(Zp-app$ ,R -R  aR0\oint*8a]op2- Z# # # # # :# # # # 9#$ &#(  #, "#0 $#4 &Z#8 t*Z#< ,z#@ 0>#D 61L#F 2#G n6#H ?#L H#T I#X J#\ K#` L%#d NZ#h P#l '  # -# Z#    'rM|F. G# CH#2E$6X7X8 h #K99s3OZaNbNiPZ@ret?a??Z\ZAa[Ab[AdterZY-ZZQ,= ret+ a+:+ZeF1ZQ!7"3Z#@^5$$%#`~b$'$Q#q$$&#'eSJ!~H tvh $" ""(xT,#(w)#'eSJ!&B *w)#'eSJ!&k ++, ,.,R -h -nowd,\+~ Z/tmp/cctYEMTs.s/build/buildd-glibc_2.7-18lenny7-i386-lqGp8g/glibc-2.7/build-tree/glibc-2.7/csuGNU AS 2.18.0% $ > $ > $ > 4: ; I?  &IU%% : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I : ; I8 & : ;( : ;I : ; : ;I8  : ; .: ;' I : ;I.: ; ' .: ;' @: ;I.? : ;' @: ;I .: ; ' I@!: ; I"4: ; I#1UX Y$1%1UX Y&4: ;I '4: ;I ( U)4: ;I*!I/+.? : ;' I@,: ;I -: ;I..: ;' I@/ 04: ;I14: ; I 24: ; I? < 34: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I.? : ; ' @: ; I : ; I .? : ; ' I@: ; I4: ; I? < 4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/  : ;  : ; I : ; I8 .? : ; ' @: ; I .? : ; ' I@: ; I4: ; I4I4  &I4: ; I? < 4: ; I?  % : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ &I : ;  : ; I' I.? : ; ' I : ; I4: ; I.? : ; ' : ; I 141.? : ; ' I@: ; I4: ; I4: ; I .? : ; ' @ : ; I! "4: ; I#.1@$1%1 &41'1X Y (1UX Y ) U*1UX Y +4: ; I? < ,4: ; I?  -4: ; I?  U%# init.c /build/buildd-glibc_2.7-18lenny7-i386-lqGp8g/glibc-2.7/build-tree/i386-libc/csu../sysdeps/genericcrti.Sinitfini.cP!/!=Z!gg//L!/!=Z!h /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/include/usr/include/syserlang.cstddef.htypes.hstdio.hlibio.htypes.htime.htime.hpthreadtypes.hpoll.hstdint.haprx.h`zPhV\LVjy.m!-0{t,g $Tji~\DZlt| .f|<t{XL{t00[0u/tVع=y'eg==9>;*/u`<!t_f!_<#f-G03gz[wmW=Kwg=s}+ih MZVgLgXuqt3KLLr0//MJ=e<k<X=jf=<"<=e>Xgj<=<=_XtY=e>Xgj<=<==;=-= .he/vsgTN1o%gYP /usr/lib/gcc/i486-linux-gnu/4.3.2/include/usr/include/bits/usr/includetimercmp.cstddef.htypes.hlibio.hstdio.htime.htime.hpthreadtypes.haprx.ht=/;0q7&x t=LK=mdPJ#XvP& /tmpcctYEMTs.s|!!!d-!!!| `LB A AD AB DЈAD DpAB I JAB pAB EAB F AB FFF AB | 40AB 4@AB $4PDD  F AB A| AB  AB DЏAB EpAB |  UAB D MAB D @AB  `AD  5AB  AB FLFGNU C 4.3.2/build/buildd-glibc_2.7-18lenny7-i386-lqGp8g/glibc-2.7/build-tree/glibc-2.7/csushort unsigned intshort int_IO_stdin_usedlong long unsigned intunsigned charinit.clong long intERLANG_RX_IO_read_ptr_chainsize_t__align_shortbuftitle__sizesyslog_facility_name_IO_buf_basedo_createbytes_per_minutee10_maxpthread_attr_tpacketserlanghead__subporterlanglog1minlogtimeErlangLineserlang_setbytes_rxdrop/x/scratch/mea/ham/aprx/aprx-trunk_fileno_IO_read_enderlang_time_ival_1min_flags__s2_lenpackets_rxdrop_cur_column__quad_tdouble_IO_write_ptrErlangLinesCountadd_countreventstime_reseterlang_addtickERLANG_TXerlang_backingstore_growbyteserlang_filedummypollfdpollsize_IO_markerstdinerlang_time_end_10minbytes_rx__suseconds_terlang_timer_initlast_update_sbuflines_IO_save_baseerlang_backingstore_openerlanglogfile_lock_flags2timevalErlangHeadstdoutlinecountstart_timemsgbufversionErlangModeerlang_backingstoreerl10mSNMPserver_pid_IO_write_endpackets_rxerlang_time_end_1minerlang.c_IO_lock_t_IO_FILEerlang_time_ival_10minpthr_attrs__off_t__s1_lenerlang_time_enddebug_modefloat_pos_old_offsettv_secrefp_markersbytes_txalign_fillerpthread_t_IO_buf_end__pid_tpollcounterlang_mmaperlangouterlangsyslogerlanglinemycallerlang_postpollaprxpollstv_usecerlang_data_is_nonsharedERLANG_DROP__time_tindexpackets_tx_next__off64_t_IO_read_base_IO_save_end__pad1__pad2__pad3__pad4__pad5erlang_findline_unused2aprsis_threaderlang_rxtxbytepkterlang_capauint8_t_IO_backup_base_vtable_offsete10_cursorportnameerlang_backingstore_startopserlang_start_IO_write_baseerlang_prepollerlang_initnext_timeoutbuflenaprxlogfilemainepochtimeargcaprx-stat.cstderrargvprinttimeaprx_syslog_init__PRETTY_FUNCTION__aprxpolls_millisaprxpolls.caprxpolls_newaprxpolls_freeaprxpolls_resettv_timerdelta_millistimernametimecmptv_timeradd_secondsnowminus_targetnowplusmargintv_timeradd_millisdeltausecdsec_nowtimercmp.ctv_timerboundstv_timercmpresetfuncresetargdeltasec t tuPttuttguPPXgPR#RXgRVS4XRpqtqutuupttuHWJWtt uttu$$VuVttuPulRuhS>V>}W}VWttu>V>EuEVAWAEu EW;SESttuttuttu $t$*Q*+t+-t-duttutt0u01t13t3u0LLrSruuuSegRnuRttuttUuPUuUu KS`atactcu`nnSu`nnsPsu `uttuQPu ttuPPu tt%u%QRu R%u R%R01t13t3u0TTup0ppVu Vu V0ppWuWuW0\ \ul0eeuhkuPPPuPPPrL_qs|PKQ\bTVerTVerrr|dh.symtab.strtab.shstrtab.interp.note.ABI-tag.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rel.dyn.rel.plt.init.text.fini.rodata.eh_frame.ctors.dtors.jcr.dynamic.got.got.plt.data.bss.comment.debug_aranges.debug_pubnames.debug_info.debug_abbrev.debug_line.debug_frame.debug_str.debug_loc.debug_ranges#(( 5HH1o$;   PC\\Ko^^*Xo0g p Ȅ yPP0t  LLhh\\``hhppttddhhP U(G?A:?B >F+VEQ jeCD Ft;{[%sGE#sx}q&wؐO-XV_W(Q mU4='z#G,s._Πv!'"yw +kG^}$<84Kupɦ7-`T)'G$2nB/>2^±R[q('Ɗ_)TtnȽ{hH(&'gSt Vtx"J{h|,>ϼEOqgqbˇ2ݍ!~r a.wTn,p˳_3Du{wsv{~#C1pu$ "tT iJrb{.Ly,B)&ZtN'TL/X*L&q¯O 7qh T|<1S2<$"Orqߔ[: ;zmo1_S!||=hvRty[ZXYg>Zsb$~Ssq{4uZ1-tyt(~_gcxI}|bja8J!O`k5dxkqÙ ̧`6ēc-O"$S9%Wzۺ;$ҳ{_zwN ‰ iK|zGמrb)g.&V*o{_0M{N@vWV#HS::hiO(i~,jge[ru2}^Re+:jtBl7[Bdv.nv1h%'ht!uy9> G~e`2ëiLmZI2bLTdvNӝiVa"%։eU)[n [^'UkwLM+l]:$O yn% c%brlfq|OeChj 6 ݀Q[Xr1bS}iϋ 4BmN)._YhLbnca$.RrZEx%Oҟ 6zT~Q 'w{쒑(153@A^/{9w6|_XpD/7/* |<;<]gANW3uXb3IӫW߯bC36R8)̃Bvr&}ihDZHVlM<n)S1_;85ElV1Fšlk["ų[rds^9}Ӥ%i@C[?$jFSz􎇾8m^~,\ݣ.]ԫ$oU2e8=V[x󷫸OO lmG ҰW48wݫ;x/뤌q=?HDKm~2. $v C+:i8]7?^(TɟS;lY=QOU7f Wͦ Z_fVA 5CR8 JV{)nKͺ=]{/1>̂QE7c?bߋTNAE4 M߾}zwU&E~߫L'+`т2qv=M?w̧ܢ +VĊ$|h޿E_0D)1Y1i-egnG/*_ݺ}K+_rv1ۿ< i@Mcm&Y*}# oby {Nzf ?.BUn !稢=ۭ?^aQ~a" O3{)NS좎KFKә6j܆|5Ww9-$kAu‡-Ч MEg\YAUw˾q[53o%L[ΣoO(OU?"$fxIưkpm+)!9a#w,?͗ ""=ωSNv?Vbрf Jz2}Aqj:I:}qOٹ|NDRZY<_л>FOĻ#ӯ_ sDCx>$9ql0"*QO~\ZDG|EggĒBW̾SJlL]@)&$.ѰE˳jWb}ƻcwmzg给[ЍU=y4gyKħytM|&Dkg@R8Iޏֻ)ƅg{H₩BC-r]̸^3Nm@!wh~=_"y]d,T"U+Lz5=/iDnjG.l B**l!3 uŠx_W߱rBoטt3? a-\XJʐ`r8f^:Ϡ<ZPV.L,g=>١[k|^()Jvg3;/1®gtVl"AH=AB]§q!=+ :OYFx3E?_@;<cwլ`&gCl#􊖈h i,U'gd3Gȭ/3?'ږ#nc;I .UT]bd5v~ y1p郧EO}kFgɃ'BcM}[2mRe 59_;]+d61{yel&uc~`i!UA b|7B}be'fd7L Bg'vSw[1ذ?|BИ{:S=`ҳUbx'pBld-q'<S欳RqUCea0S7YT>}4Fj|pHF|\0%xE6#JxŗCbe1ܑmw}ÖMb.xp{/E-of8-Uo>;/2(TZԦ@`m9v'7 iOv s7j3aOIF:5WƁN"N˶JkOգP#(d̿?nN˜ʈں$='ɣLo'km;\ԊqR*c'*6nܵj9ٝ886?CekKVTG3O1~tw bwmtqjyc W^{;Jqz(` CwsItynG(:iqI<׆;.fM:fe-9&;Πװ>~8/A`Bi!ef؃>Pޟ\uj5ᬺՆ V|e̷#LkXDߑ GP)yf aLS"{?ٓ(c~c;?ݙ3|P: GDyvBk;ٷ/:jm RS%DXlG1PASZӨ[p?~#g$~*yTZvt+.Pq zтBZ$|{ݢxZI>)q:%beTJ36Xey;ZmTҡ`_ye,xyl!43*q>nyU PX}z&Jگhwٽ詛wtkV6ej᜽BE-vz5Z*~"#RFozEA3q0I(KT!%@ R^=Ugz7WMb>{F2D[gm4hzӮf~=«h=[|5 XhkZ'i_.*MD_~`'$fCqɗqI\b=z:^^zP:vwjg?\Iu X' dE±tt;XOd,5L `p)0Ced tK6mubߎI1S/=Ox;Ьk2qF_]Djj[u % a>9BOK66\|z}?C5}=vS q=1O/9aY/{~PC6 =VtTc5&qM5}w|#mWCfzƚBBį"Y'ϖV d,Zǰ4:0/Hk vtl ^a}t=-7ޛ^uS[rk~4r]b,\\}s&7iI.=?)o\;[ yZmww^c^Actx%)@k~k oVt$uZž: 5-RW}⸃z}azrs0wi&zmn3$ҏW⩙hgAqqLo55`$5q_W6m8Vq]."uٱMrKpRkoEs<X1"HiM~s9}=☕#/%:nږ-rS뷄MOyW\_ɐ\h݇_y_-ibVes@`1pWq<~aC5 [15eK0B6IDO)tџ;~$Q+HE Qns֧^OX>eF.H*Eɬ+-ah#Ģ_,|W*'TbEdV0J_MG/_2Yn TkET0i Ғ$\Ey-rw0Y?_7wӁEKʒ=O-2IjQauK`uߺft;ќX"B0)g؊Vl7#JD&L˭?5yHc8oRaO",4n%ùܾ{<:'!FJE).B,0o#qqY x?m?܇@w!65ܩw/q`iOp9 eبҝgnop\T1ɚ:(7@CRSd^U,Wz a;^0dx*G>aβo2'_aYԢ j~?xw Jͦ*{Ʒ)tYLњrD$z9z^%e;6"و w pZDHYxR"yDNz\tfCӹT.K (<Ubnr}][ފJcQE{vo.ͩ2Vd :uR JAwbճyF稫H$GMQybe>EZ܁#5<]2'S+.;D):@TRLG'+DINJqdE ,CkM9\milӱ?6G*"?:qm(F=fa.ugT țj W aU+wo*x U*5YiW(BIPه 1H@[~~\.{+ [rGQޥ{;4A1F9VfǢ2FVӝm니DwA 33?X 2/ Jt$ |qwnptO>4C`Ђ>ُYI8$t= Bk|8UJ~dez< nj$9"Mvo\t8edԁ_ֻmp7~+~/$dw @]SF9φNu'{>^ֺׅ)z:_K~4DޡFsogFH6b bExod>y 8N@:鑱kjݺ1E< _DPj~Kk*M?Lw!, Z lhhXѽ |6y_-b4RSowə8eGS&ȸ ǻhjP\qq\Pu輊J{sא>{,Yv"l::c /u ЎOV .oTbgX2 xLC9(C)MoE[Fcs]{g__ӊix0`a&WQͿVJ0Gq&3ʴ49a 4|v-;S]$Uef m\t-/ůF ܋h_*w}fZ(F@jVGd- [X*?nKo8˨]gzϕ;}~zeP]Sd17Dq2'=(E3$KSў}7R9)JqqE5ܞ# 8 tq%ðF.Ri s+4KO͞$Y+~hQ_)khۊn|lc/fyz+$޵j. )J}(ۚ  L$a T{[9Iͫ"}O3؜SU3}sL,`ؙ`T\>@5 !&Fu3U@l3SWLBfv [akV+W\QZC%ֶ !"G3CͩGonrsG q$?y{UOy4&RTyJս;">ztꋲ*ޚVmm^:᫵ޓm뫝[?#,'z/#u޺u֏uh~~aooPNʗ򾂿xfnNK(\_7u5Xg ,6fT?c3hQ>sq.ft{Ac$ϔ .M_RKP&;K_ڀlttxؚ;B3򬔶~Xi;Vt= ȣ4rj|l6 `XBc3A$uTC,c%-hYh9!KA$ =j%-Sً,բ495ֈ2',Ґx^CG<4S]=^:ZܪR=3Su[h|7v?T / d~X*1kHUrHŇi,!6~&9 *u\Cf> +V6pJ9gK,/Z}_boߓW9l(JSOv+n;uN':&%Kv5C$u7 ”[yl _Ԕb4^^v:-1+l5OHݲbk*8$AT`: kXc56kq+Nu$-@ʱt\c^ %-*/D>*Ig:4yH!#ceaW P4P)YhK_* Wٶ\=+RBybNT@C;b~޲[)o]?H4Y]_ U{@*S{EvxW\ J [ăKsfVLα2GH7%J-e4>WZ@}Ss .rk;1"̴52MABBK# k \ks;*Y(53r,Y_=|Y.q=Go8hE.BZZ &#d2>lh$D:US,ՙ? CkHkF=WD՟trle4f OgoKG@Hs~VR +u8Ip2P^jAwdNjrDd8w}yv0(spF_ t><`8>l)JǼDOEO.nXݿL_} 5{E1 D; B%2AEٌ"gx!m}Pʲ]Cb~98^f;a+_P1ЫDQ L?7zGЃ -A;}󮠅ìuO^c F߿mT` ZEbYfk$ś+Gaϱ'pT2[jyh7Z0i-R\qEvm4&u!'-1 yYu wko,X! N0|pu/-;=6"VNXࠜ^ e*N Y+3e݉P{;7q3 Mx 79+J' `z's@џ e'R+ ]~*!-yO|CZH:}4{]`o:]'w&.~_+\3ettV)mJ _xOٽfDH6@p_C0M/H·,h$Po@3هHuu2q߼?c>WR ϝ©+"X4SzZAz~ ۠-7*lڦ?\Qݰ.#4a bFKOo K }eภ˽)O ]N4߷,ɐUؓP 僨//t |RG'^jfkN#% s0p~ $طIpaU(Qii=:5(ii8ZFGy;vEWsRZkDaVWY-]PL/IE /ٶ.p۳vX^2% RG߶A>PvQ@V;kL>7.Й!2}(Y:B^piWNb2kDT8P,$%c3ay!RLIc,HZmXti67i_L%&ȕ hTto:9C6b2^(~~L|j,Yxy+v%N+̾~$ī-|oRw+ L.xQ|n8ᕢsx>ou C|kUh[쳊.t3g+D5=5ˎIiiʬ__+cIf/$&R$V u&5wRb# ^ej1sӲtxyDzmUPdKcTVK7 EC)Ʃ^?Tv9n,:s;m+<]nvdBw̥68,[~k# ?[EHTm~ \Üncr}zzZKhR'}޵X} 8s(M7̓c,8a\JoDcz;EW7 -1ked z(A%9؊~)R]m>3q`iPJ){tX~:>`Ε]PV1JԒ7Ž :14v["._Y[-~)l*e[֕5\ ygs&1ڰϬ,9,zx2^=˚6XA;|[.2(7"" s "H ]%4u%3`AACTIhA<҈;u_90~CpuRfʠW6BPe|/JSXUɚIArpjzIc%'yM:!p9l:Z[$uD8lu(vܯv"c@a筏]\7n>͆5-h.É$UA(hP/e1SD恞 %2_ƒ3V,_~E +vp-<ΡAWɻ~51DDH3+[2-M8zD 8&ŎbltnN$Mq./=8jmJ 280I Ѭfei $T  ؒ!r0{W .K#|m)p;D.1Ljy _تλ~_lqJS<ٽI3ؽ 9Q쪒ޝ$ϟ?qu_l{IچޗC(?.g -QnTF-xdE75{2br`Re.n( C3i;ɼ 7MQ r/l!Yrԑy3C5u-Dn7s~|W/e<9N;A7aѶSF |f~eY,AVVT$;:K*g&Q';+=/K~B7lpV|[IGR=kȱʍd9;Qx͖̕%pvN4ښu^D9)uX$fHԸ{ S 粿HzsښSTW;4800^a~o1ZJ p(x&_UU@8Ԩlm yK> ϮkFh*9 ff#jZmٸӏU+:|=Kz,"ȐAK]!7;6F%kw6lOO#Md]*h[Y;#o5~pELy7Yo`".LBnWJQwRTs,M27r`fVmREtI%Zn{i|E+㟂^C*6Nf`E4X)|#BÖKcٌ5Ziͥ XKL*z YBGl{%V!=[ŐZ(ou*QC@(./Cג%K@"R~Gq6e -\Y.R?U.sD]Zf⨡i )qc47OX~)hqsduQw:VkEAF+.J4nP#ߩBW20ǹkՀ(*" \pzVʡ/+-%}&ޡ7"k(zgpdebX84S}DpYY8`Ó<-ԻٲKj !=3T@Op"b'#$dW=0.Qfv~ro35$WS5:9V>`k><` CTXM`;!h+*brQ$2:u8v)D ,I( #V2` G[)94*K//e5{[5h9+N܁SN#yĂ2w8+ƥ3ioVYb|TsJhTeS@ PMPM% CFcN0o0/E q:NpRbβĚחpց}P4ȁOM<lE;3|h}QxZ@Xnj&y`n8`JH ]6W7Լ |nT+"սǨt| )QQQqՕnu#5V W(:f'%w'.'fNP/x%`e8%0* ڣ?aDmҁ=< iu+Ҍ+Zʸ`,ab +ZgߞtJ:b?H~d#ҫ?yH_ Q^?U0-M폾B:y]^1ڡ"֡ڢЋ&t%FkeLhn)sWE,3#$Xz=/'1WHeJN^U`-`e)+0ZAOͲY#߯e'-/9bĮ'mG1takYgCវAE5jRy{YDS*hP9Qz\޶HSb@$rVvԶxL{(~'kM? y亿G8pK@4>HI%9|bY`V_%Yv =9"TԢhS~=!@/*4-'빀W}M _Ϝ)%c@UpMvUTV '5h`pԶOH$c1$@Wxj6G֧S{޳;U' mqɧN 2z~ G"*QѴn.=,ӄMMעv;Y5<@K:^6Jvy9-"[[@r57quGrU\ :Ы)=+kɤ|M @ `)l0 a&4A2ԛ/uFt#8myJr BE%ӆ]6]/"Yn @Tʴ"\Bl+) b(gP9ˇEM1,˗M $_H4ai\؄_fGzwt -wHTU=GhdxoAյŲ0kuy J@Q]6 cRKٵ?"wЫ,$Xm6Xsh ~o8m&Ui{]RR+(Տ5*te|(]֙Ճ][RZZ:dR͝?l0DMu71 ?IP2;vBHaOq(Xp\ ;4#Z֘($ Jq)VYIŠ*YXmR"WQAZ3eD Y~DwjƯ+UaTYbY63u?C`Kp_s_r 3X{= ϐϋ7\@mJ=Q&2y?IK)›zITjao45y#_FFTKyp+ NkdxFVYcGeJd+tW|ʕOh.ԐJ}]>7JRLӚqav^8k -x j_W_EDw:4T[Xc2O&N3wm v 'vQ"f4giu?F )swyS-N0D沟Z A 1DPswT  m:"XF-ǪXzlюRI=EգQ {^O_ݘݻAVi&\8:YeV^ [LvemX},M KekjHa2puZ"5Z&C֭L->]hh!HR*nwȼ2ywUjޢ Yz8|$\\Eo!Qo!,2uǴ)kƳf{L9pu"u{J:tW# IQ䵎&F᡼ն^uk+Jr 1 D+rz+G/P!VsL0QnTWXMќmMۑ:>d4c{}bV_6PbI( qH;sAfk8EUWϒqRK"a7W/򐓥- A\._B2,LXU&NZt2y 4gf_k%y? 0L{ڬu,F)O~%A[Ռ(>尴d 7|vT}) D495gy*-3L倰:=i*[BqHRؐ yyѼ{;v,SgG0c줎)FZ\rݟbɠɰ1*pu~he̕(UW/ӶQ_WQPR+ tfGIuQӀe[9=5zļ⫚nǔ+mSBT$HR69 D[nGPS{KPP]8k-ǚk]jX'/Ejח ^gD8nͦO g [f[q;k+wOa`C %ڸ/8T n#G^Ї$@PݠFɲ:lQyG5`}wuG4~bȩSGLaiX*k[>5Ġz}YSXrM# 'ϻVI!Br% GbY8m5@=C1%%IR>Z>oFa^~~#ls*N ƴ[`ecZ_w1:EnLŶՒ7b|XkSFbYo_4@Oή+TX+%PW$n Ê\7ZZ.s>xEض;/?\۰3?i9FЋJ,u9P53ؖL_U@۷6,T͛O\\Η_y2z:e1LUR%o.`?*A'wc؇o&Ehs[}GHf?wB*L_*R},0p~9D*'@ݻ$&f^ z &R }<&3;HEf *`:0VIC9Na=\RhĠZ_8giÈ JneFL3+3iO{~ɔPY& ʙ>[+hqzo2q?ŕ~>~t~l`%k*ݣ/% 9.D&kA8IJC$=H=փ%jـFq\rJGFllYbAîozY*m=',72/E0uݰU\wA/y&8ЁJ?R3qf;Qq1n* ֓HDeKhϦ*vlNvc;p_a3*+0&_/5Z32'50^ 2{#8EQA2 $ <&.&ϝ،g%*6`)>77(b}L(ܰO aysRw+d-%86ᤄ :4~gn%zWrkA Fцz迅EjwN&"->7ŕ7JTsnQFW6U Nu:ŗ`t*ظ B _[sz%"vH ({͡ Hf {"TJ M 0XK`{ v,Ji@wdZCl*c4]jJb-D"Xԯױ 1: Cg eQ%TzcDo6A@'3Ԛw.rcj~s u .9knX\Q0Bϊـ^yV7?R;8W&ˣ*GYxT05c_2ͣnߑ4~ ) jƀZwC7;qgX@J8qH@.` eeeJdm#-+BQܟII Ą~;*D_?,h DGNd @< *x+p9jCU23e+zsKj)6B][g!2!IL Ve)x15zOpP^0:lmS֐}}'HyE/>~_|q=cG+Y-7\5OU5)U*zGiLyE R"hfqyM@ `~pQ{^c\tp XyTb-YVsU@/Y &;K an4E,@l$;y?hBEuΠaϔGVw5uifFK$.dh a[zdPAe4!Ii."dqqsYIJ*A,'+O]`)r߼JZ?0񃥌#%XنQTs<#6A#7ER9h b&:RbI ]%'& w/qɭ G%{v^@rT:bCL^f;TR7}v=M8kMZe-cdvC[XP8sQB qJEUsw2ߙ:dy\fY,)o{Jz3~^w:b^#ɂ&kyFnDʕ&@چvRAK3lCU$$V7Oa RX0[I-16wJm`aLn],On@\ԑ 2 7A6{+FP'6z-dd5'JrI lm#r5X멁iJl*Dpb8_&! sb(=% ,m,,,cKaԛS}L q xv5׸1Gc mxOi=T0, >VW׽6nL}X_]Q\&v3{ŅL"7bd$X%c.ƈy=Cv~&lϵ:{t -[VcVcc=ŭ?i5e qwԌ<,vO"}Tx-ٹp=;cFNG*/ջz# 'Ϳ_+\m߼`^(beKd$ (z*ߩ[&C ?/dQpooc{m!ߧY(q'9^D<.ƶ||L^qަ^rb2TsY@Ϻ{< *pv6k{ gp"!L͐b6פž‘y8D(GMRHgW.࿙\KD/,^XaS|x;~96Gޚ"dp~0C .T{vES!GҰF{WB e~z1ls;^ nQn^7)nښarovf&8W"_޹98(ՃMZn->;} RNêc y{q QL}y٤@&xoNwF{ *㾅@(>_UozCM'-gW%GݠЈ{np0zYT6wƾjFvmgm]:(U/ (4D]zH+0 `wg@F3nzkwO_Qg Z Zh`&Gְĸv9 g.2;(;=\e1 /IXg>@ ^3r>i?5ˉQ|_$T$긷*b7@cclm,7i>f,"1.yU,ݲ$49+&Q4NmDɹ0@;Fa{w/T ^@MjdXwQ[yH ZX|M [lGd<{nQSP/Epx dlK^%^ q@NpƷK ҊZ3C_P}zo V7t#(,@1U99t362Uw¸;%@%e߳u8EX 5/tq 21g% DlLQLӎ6 ,IVctrՋNER1L[NHG|>gzx{y <* 2Z{l,4SETQ8/ĸ}I.it'T:gbLwAQt?1Z-o݁w^F7 bc9['KWbG 70gHxeG…-+FTz`CRVI|&6W)/ BXm>xϤqm+$~ ڎ샘%=o)(33(EA585 \٬k`8~z{mnK3gw`<ԬJ!kk<؞>$Dy.IeW^2G1.Юl+e> 8n.Sb1** J"zH47/&om3id~kܭB?B3O܎OԽw:ޣ1z|=VXG9% 7k6{]D;/h*}Zb <. /Fo^>hix$+6&NX%oҩB%j͛$5龬];";c7E'!XX۱_2^q#]% 2Iߩw<B?T^4NtI\"eH fpo廬.%gVsOjP*4UEbcTtfv.D MA<OI9}!Pi0=̭7oeG~^+uc8Pn),Pu鿅 vGlHխJOJN3x͏?ʚ:^RmidW`b4mG$*wl~sn[Tuή]uΒȫx]!`ca+W": $\'BoŮ;yO&6n4Kn Tǝj D"6#Q'k6zd;ݺp2gg{q_; (*sgwRFBE[C[%B`Qc=S:-vQv*0Mkj/Q+p5m_ eOa0R~ŋu5ǵ*T,ZYP dt8x:p,3`.k[o-$_1uu| " Y[$6AkjOsM9aHg96.9AgOD1(E|3ƗςcřF? kؿmP-+ExЩpK6l%''m'K 6vD fɧ%o l =`PNdyYY>1ybԥ y$ܸpi+mY72 5x=˒ #?+(9\{Bi.>`x8G^~@jh0޶;|'@\`IK3K]匟?>,GF>`eA}eԸqy$ ~(9zD6!nuu{ÍݱAnIK~^CAM>2Ɨ\gVv:0pyŁl!`z6SpA_ S#D 'Oc(k ˡSNlы8zWwp&R^AZ?"U~wFp9Ζ{c`gIu3}&-?ŝ!5bMz,gFC-hy|KdBfA)0LS(ݐWvʠmXpcxzWrprzI ͜qݬ2:K΂\YjJW 2ȈƢ-Bal}7瑼)+iOؾd r]`\1Üi7rk;4gF \Mp>:ƘnQxKzb@dHcQHyC-ϟIhF]oZLhڠ{Zo3V>$en%%DzO1p MO+{s7y3k IJen&1+loM#!e; ÀUYZZIfmя;wqL¬ݾ 23vo(]$oE"U&dRONR%x=y._%Wt#s}]/}%ch}ժkkխ;`D鹼%,Q֬MoKJz<=p?QPf@7Ο5 HfZ`*(2&L4:Lޭr3.|BĮy 1Pe5ht(_ RK@Ϗ~xWǏC4)h& Z, r;[ >ٕ4j]"Ag~j`lgaՄ_K1.1]ny;<~\1d_ArEʦqG/zBᢳVv Ƚ˴ZF)g;ee)lɨiNTϡbaaI-^EөY2 6锕TC'4aߜov8q[k-MR9$ mV{9}4#]P!didmKq5),iyG]vYR033agw3jv({@@ӧO;cMYT`lJ_Yv\T\ !bv3WJ8Z;@AFrIFl]7^+[1<\%l"*_\"ԭڶjh]}-Nunl{}7؉u7^f2,{@3U9\JGȿ+}|v}홉t_V~\38-gx2믫v3xUh)W&(hL 6i`0}:$kj:JRv 7]a): 9 koMF93l׭G@Q2#%zRШ# ǦKg5htC=ASHM=${ WO10Ll}/fǸرH 9W? ͒wy( >:/c_E$LSm…"+吤]) %nT?^ݐa7jesuqQl6">\Ա6Cl RdG6&c{l{u4F 1C+K.x,yu\Z>D=fn{˔(˼r+~iܒG$rڮ$$1&U" iOPlj|d>.QtT*Pз(/((V;7T7 ೽>V/u=aZ粘c9,]TLbSĚV6Z_E' '΃26iBO }d.~yaI\&iΎG#\8hȤD07|8~﷎I/j>Q$q u] gPwig6@ڐmtE~94%6}tkZ,b>mx-V :Nqv6x2}SFiq" yzEc3 :W`:`ڲ;_p𘘕AJLX,ջ8 !![xj]2n}KsdтR8:Xِ\2[NO <:(ޞܧv$`wY\#spGBk 9ii&hBcuO9'o4lҨ;9~QHK 5_tg(HkٝN }(DF- i^E %̬'Ƕ|gR H_8Ky.,0I^]<|NTOxb6y<+Ksb`ݹq/umō_.8pVAJk#u:%s85L>OpS1;~.<>8ђR$EzId f ٤ (xd&D+ my-8IK^{YCU{x()7cۜ(ĒH~)@SGMѣNO?d:"$?l-eeĚXŽ\*14ryzѧۧ zJ\ [qcC2ۂfˉ+##TjBTUA7[X)t_6go; DT! l֤O⳿<= l>twu& _R,֑Dz5/#cv>:tNj'/rm'b<). ب<݆Ҧ%OD4l6s'K~˽ `F)st]ĨjF?x\e|&eV݅{ISm=ZL,6z䁔V֭}g18Ͼ} kzZ&`]RO@4m%nDGJYekUv1~~]XYXG,MWճ\j [EqǫMw`+lqWG3ļ4h`)mJmքמ|nM~h&r>!NIp$^-u6So"e%x UzLDyMxhՎ 뢡f ϭЭ6;D#G_ץ q jEi4(7:mKbY-fFYb,ff#wzzg9?"vlX{pԯhٰ*kPL6^&|h9\V߽[s?jeJcpc6W&X7,Pxa[b@9-G$7cim^QUi$6SNk 数޹M*[ӿ1BOrX43!0IZiWxƼ._b,ӓ:%fd>48F5"D,4,,N|0Gݹ8X> qǕEJG*g(ͼ2n$+m{dXV*gw,lƒlOqq} ÍC3cx2"q(DVY3xÓ]d X׷1[PkS.DZ|gƻ{@=DjVpXpTT<2uSNs߆ wIO&&rG~ Blx_Ї$TI@҆\lzfwprO!`\OmQ#𪠠(o<':- Pz2ݷ/Wz˓د=^fWK{i\Sٝ86^H;>qI3[Ir^5JxzE m$ɫUQ;N/RCP{~V4cqJot!! 25۽,IG 5@u ʣ6w:5 <00ᄀvFH  .~B Ji^An^H!y'G˞SlmSGv2P7rV0)BQKЬpҩKb_TvvBRv# Hϒcޭg4-x#?"F~!iBB| 'YȺ.A !d߇^+AmJ\w@f+۝3;pEB6"!04b?28PG]#>~ܩҹCW,!Vk:U+XzMG^1] UhhdNzxfwns9999N;l IѹRz1qc-OrNG7QUv<]M7_'Kb"83 卟z|֦00 ~ 7X ^?9vF$QG+L`CEtt֏E԰_6']Y^Ue/&}_<~j'/'X%;HŪhx'j$ 3K;"1w"/eA\v atuE SacVrȲ`;[n3~ۻ?b( T\{]#A ;Ji8\YsJ8$ % ֿqCt_ikoDOREQlxc *;CpF?ks Pڣ/#HxlꖎD x@Xqcp؞rܟOezXf&K8sf'Tɿӄz]B9 FYQ.Ǹ/`5dDxvRΊq-iyf `,ړ4lHԉYRmysAxm;墓풃&#ZӞ!6|zVUp1gv<_ះul* _?H_5t?X6ܟܥBPU,q O&nskZyOP.?I?>; qOqE#.YSظ@01.Z>0|lAX+ɟY?AHsGcsO>x' ,w29fddy90son,;5L1?\;\$ӥ-"sf#uL^eFdI;η}_7dԊXW2uĿhY;Ng|kHTԝ>tr2ujaج&t ?gc`@c; FboLuu4(<dUj0?HAEmcLδКKQΤb9Y7|rYgt+A9-ZYBO!F[ XbM.Qߥ/:>7NƘ/GT6v%zaHscJ$y5Ov:S¥x*h2B6 dwV!t'%nAo.MC # ])C0@鄽; ~EF,3G@쀭>}F4aj ~ әX*ʯ razdHw*er?+'\|R>~s lL:LICJѫ$Gae(Y)7= v@΅p!Y~3CjqG>Pԉ_.XUE0QwY M5bd+[ |`B-r\U8ߗ96* \k q?-bddq3=ڢs<`SPb1@M,. i&}ZrFi,<oxO{XѰ޿4X͑XWY8xI,[擅RW/,cʼnLX"fAiH,~U/&>l\ gh;9.u r#:]n@RZ9Zh,U%)YO]ԡ^.T;2 zۧJׂvKv(A$ݧ(th!_kE  _Zm$XT=折UWSO81틐).} ^2rPXVKK ͎Uf s;)gNzgpY4C;y(C]-jGɫR[7gHbc2XWsՖo S 'H؋;!a(RDY56Oh̀v͘F!G\35^spy~S#x+i'~wBX77d~%pjٜiWnB;0v,wyƁ UaеFccl#*>Fh^ĐQ /aHq)2Q6kyia?Ny Ckc_G@Cc 2E>-mFj)o-43f:ű/?"M1ZM/q\ʩXok[CxϿa@4ªTʬWz>ł_@@co0[*"XwryX:"MY FJ͏7ojTjȨeaJ.v9 gk d,WK?#``..A9Yjyp8!@L`36pa}5iYLbn|7M9/_ZxU y?:*\7m#R[rrs U_nz\{ƞRv*@7.U,y'͗jHU)Z+R{2%˗킒#m$AlyKmbJ"ea2H D\j/A(]U[S~}A3U3'B 34e@Q¯O4)Z0/Qo(g_= 2Y_\7g?gT['gedvwU)sx 8oOr+HJ~2ٰAr~b}gR 3KZk=o89Pa1%3|tzٚ w1v VWGC6q2+k'ߟf $*[<ݨO%A&*.mi;,3$Ŋ$5 'gE.,6ʗәb+Tpf["~O\ֵ5g,w|[_ ;Iq@P>0Oƛ&qS_/[F!.pqsm}Vl6bk[ p>RՇagCDi3Cʷ3 խ?7''JТИ 6c:ejIv{="1lb@@|M^NFԲq L\ч7+_ʣ7BȲ; ۳O\[fEc%AbTx^-rTTu(]c2ܘ"8ӔPl^GD .ex2%reblAFo^3(D V}lx"B!d:TO >ɡ@Ē9_~` Jgf4Un6!WI`}aV&E2?9M +om~ggʖ2S-n'A҉ bPM]r eB[5'Z/^dLL@tRp.ǃa S lo ^,١S|y%Ta&!/df7OVYQHNOm g}«±)\.10ی&&%`1E>(;U-o nۣ\-Lth[?6Aw_%d/|l& ?o~?)ZQ>^-|}hJ΄hTK+֚Oҍ篍N_8mOWm5Ώߝe L, q?!bƆ*̐VV^N AB I+hmam`oH+ddo(/"TUe-+=^XS1T.p^ :0&!>Ԥ7#_;K>n֕ܛ>#)A ȁnZ l/OO]d7~AW $5hp)=Ђx- CL 00k*5fxNm3GV|/jiGrM)@='VC hh99aIzK,$TSۍ\w:uro-Sl_ 78_HH=s59`SxtzVBemvj" 9_ۊZ`݈ q)6H# u*knmRZBcjP<ӡm.z}p :9r}= pAFj2 Sto磁clZ`鬺ww7x ' ^מb`W?鵃dFNéLקe@"ӛ^丑ݩ=?Y Ci8ƦBFFxeӝcWWw1}B]8TTb3RSz'm?N_vQnn1; Dx66ZZ2;5TXmwa=@צ^Ü Jk!Q7quɕ4|dsAhO*TfJlS<  iБ/z">#Eu-˜k7Sf@::gV~_ ?gX͐ - Xq7&/?WXpۃ/}Ж#]&~i.VԷv JO!޻!A:(6AƱ]"Nzκ^mjFfjx՟w$/ޟd}bML2*nzU|3@LzdU>3WkNU78V,_k ã;V .̧Ԭkk K0c* px¸\ygK'^n%d<hB?/7,0QSkj[-䔪TZYX(&:VvH.h{sSP)jw@||ycM;jy`OD2+(Yu[ք~a8k(f*@nҦ5^JRg6'N[=̖j~%&WqGLk7Qo:0,a4#ЕɑVb۟ )aUEl$d܍hnoc* LC[/?Q kVafl -:C2ڂ:2{-qcu]3l/ im=vgDkܣdoʉIQ/>l~x]"Rqַ[곉u뷖jߌ`V%1.&ښmk{| 3ZуmrVf%Eo+8TXF7%HSlKU*y;l>jSs8a( 4,^SMX˵:ѪKi^ bNY=PA+ v%vOG>;I\MolV_+16=ouOur7hgFI Mr^/I=_@\+@9·Ql٩gO6, ʿWq@}NеerC-LuAO 2XI{g/E-UUj2r%Կ&.[^Ʊ Ìc`8 ]~w&AΙydQYD{!2$4>\&ȴSbsìj:(L~>-r<^uM-3&ٯкZ3 ՅTnyVF\B J)V!lթiX03zI*N_Ѽs`[^ٚ1CR6 =%%W-K Bkexl%GdPׂ7e ^+;#7OIꎋK.}U O/yq h C?-ghi;=ZݎW]ϪǬs+_*H{m,)C[^k gJu"y vg(΄6q$E!>:e0Ʒ/5yL~S:KÁ)=UD(nċ<lI֗.6;j@ yMo+]%P+w7cD[p*Ts9%?˯aJ Htww:t4BABddoYshq YhQ̬xJ$^>%{%7:ogp&im2r B$2n^nAeDgymx(ЬyK1HoU( DޙlMuF.tp[iόY羅oQ.bWD8G55nP_ Zk%cJA1bԉL!HbU}5 oķ'3Q2⩅W {i*,/~f(끴 A`mOFjQ+g=|:x[dۑ/kɵb@cQl\xܮTq*k=ǔ8yMS)Y@Yc4 FA Y)15|i)s9ORJ8{3 Pu}͋ht>"g2 wmҎ ⧥lWcIsC?_Aߟ ZU,e nFyGмݦo1R+2FV>ډyJ#NUrWE;/dV=}y.lS52E[dU؞r٬"_P?ȽB@]qiBqou-i$Dhu_۹0HyICl f̰wVI%)ZnA?[]\-=$&=,TygHd-]Vo8>j_l1?uhHHJ(M/<JRTmkx]"=pjՈxqQazhH4J¨s CeW{.nAbQп(ڎPr8;_ע)}l|^U~v;= `~HKMKmyc9c,k^YbG䟖+0% z%"\=e2v,,-Z€RiŖ4IUݞhL|p(rhVm@Td?4'o;YCvۦ+k̲-ۦ 00cU`>9H߮[DK00%KÊnڟ (d4_ 6w 1vHF#nV.28'Tlz׉$F╎zHMz{]о[._~-̗hI kă.m#&c2&nM BהJn6՛x'J&m&VICG:͹馼FS *ۀYfvPZޱrQeze|/EBVRJ5 [5nŌ7DƍU'\b/@oLOou$\a:ݔ-RSu8 #uS?<=>A@Ȁ)1kk`tZ;JeBč Z&1Dgktyr&d"3y={-cjyϯ=G>g6} g#s=ei2 9 /Uh E|"Ƴu,MT^.cO8<Ήxpd/U.sA̬cKX(NyP +Q170ޚ}^\GiS`i✾TzƯ srwOTcG;LX]Rj̣}`1E[Gf?Tu-ȸ(Z|J='mCZ̉H[0 `I7?؍Fl[`@WԊyaXs S''RsFR_Gꗄ˂Ҳٯ=/uq"ϺTkN"&TMȭ5DJ9!S볔 t%xS٬ٽb3UKwtR ;UWW_!vI0laq%g+I5xy~z-bA@ρR$}!y&dGQ:g52Ed0B]ڟpg̺N-3ٻKKt Cf1N=!*+]aVc ҫ#[Rţ4nЄ\4v/ EmA  c2i1<j+YIKcB''=3e_ދ?p~H) _]OmFt%r; dubxFA(4{mA'0ؚ8ʊ\4(!hj̀J|=X[<̲{BQ:5©&bLb}Tls%~_yrkav=MA[< [e\)MGꝐbD\lL)ty ..[SOzF֝wu&N5uNI;}%zo u~/趩.gw7 vI9~-==I( nzc#YR!w_C|P/Ã0d#n)@\z x#5 "-X cO F_@ 1 ._x%yJ:RǠAx8:|ܖ"U* 1u#يhF&Baf*r`m%9G!ؠ 7dU0z,F[AB qܪD!.1cF뫙bٿzG^ 4UT+iqS[nxb5h:f4.@(a⫌CPa='K6A,1<vw7jC?O۸NCXN_:yFV2CM7rf :_ _Ħ 1 EWz*sXK=I\C:lI,ĠNÕ2NqI pNZٲ*@EUDJ|EjXCd<o;o ?~h8Jo[Uuv9hv:/ Qp@-n=]V 1,1pX[ ~$4/Lre5 9D;#uş^A䛬v )=*4iupс~xPϛZҚ?^o#-O}yyy S)ZKvaUTLĸ9muvPp~{dD^ԘS3Fpz9) LQk2Qxrs]lsuɀ凤˛] /ٹѝnߞ$&LF^Ks{AQv!P*\,߂SSs k滛+;dcWPoBji*^E6 VU{C)Kq& Jq)7 S^ =0|Aw | ;h} 5Ih'rIbбa8)A#Af+Y)+~gE_ $fێ8Jsvyq{wWW'arï3єyAzcDXHlbMr0YbO+n +7}EJ[ᣂ%4{ן6)aW7w<evL& $awH(BNw7 (KʙAPs{.E{R97.ŭQ2۲5a+aޟ!G+={Ng  8 d sa b4ێDU4XID r{5j9Pf.{ɱpT7p_>M|l={߱;6"5N*UjAm(9]j+{䥏4x@Al4 VproC># IJMHnC>!TI/ٛ/`Y^Rb#1Cnkw [KF[%+rL=m϶چ6a68371yБ;CLi64zcG1 86t (()LKSyKw xں_ x'r t=cz% vhejDO4-L%βvK0$V̔bȖ\ ѮgٞmșG&sz6wp2_Ycx ?/cwF>L0018I`w'p j]in)/k˓}]2өNn-؀WPo oӲ??W;{h=PdC)38ReK+Xģ]i?ADوU?}cN \GR3 0D6{j !}U>@HC#e/c`H̽>J MmEPc}c?[n__G*8!2JB)|3 ʠl9.%¿la'~eo5ȈoxJZX}@ޔ"ޔɀx["hU GW\KeǀӺC+Xpc EZGa'6!;8=9U͵%¡s1gz#2j ݤBx̴oGs`Ă(48eE ;cٿuȃ9 z])y2Bdujp(HAd17TޡI4B 2U0$P: _w╍@h3TEB *R6 ,^rõJ"H#s:{DI(FI/6vfSÅuǫF(*ֆ MdaCqfOB'AJ ]ZjzFդm1'yQ S-&:r 5C'Pz;_P8~a _xc}bh9Ywǐ6Hk}3J|×{}4ϥ۹tO3!?j5YNR6O, ;;B@q!  C™<aB2InYb+lwvI1}>0Ē>{}D2aJroc_7PtPtw=|>3<£*3<4fɴjv-NL{6Bb^J!ZRRO$ T sY{|K {ͥ{@FW4ؐ2n^FgBY&)f$X=!eK>Nr/u??Civa9*?>-}o\گ=hI\TR+Nw:ނOo;]@& -}#s +v5ddW=G0Οӝ248NvY_\z3]~Iӟ'? ΍'Pgl i Td-wST'tf/>3 gv vظ:Gw3z>x6F yPP9&rHW睠x#}ڇ. H.MYl%*SMۘf($~[1h5#v}(kb^=)]f<,p{eKEf0̉"q'B rOpe,3Zn-WGI0Zҳ >ɮoB{9s+(\x]ƜMRDˎw-8*-WDkJUyf4q 1=Q,Ruzq}eqmK8ɎB#G_]/uA pXMn x>)CZdˡa6NY 2`WI"7bVB F^ʩ5I]FNi=_V|5q^|S|!U!^E@Σ)Oc 1bŸc' m5M۲-wD^٧oEzkQvwOP);-v.WTꇿ.)= {3@U #44zeK!7>urĂ6̀q$ .+W;dS{K!j f; íjOBELH\ݺsoJgzH4vڰwD/mzOan٪´㘥u@DBK)W* ?XYj{Rq[G{4A/?&Wa#ꌺOOer'hJe3Z紥hkyu %H;.K{*^lX tƥCKUH*rAbӂ.(8aH_-b-m3F=Q0`1OEx 6/D‹$]wo{vV+Hem?z *jy ]ADMMQ-kyj=ƶk)A1kx&64s qx&d.BRǢQ$TAQ4D} {MvDX?-0ͭ# !=`+eO "ƣUq JB~?pvr09:V&HcC20!myM+OzYW+TWauYY ʣ%\$ 3O@L<*$թes0'1# ~J]D(3/]^$6W'Օlg`Ե)`vw&'7F Y_ޑIW j0?DcZXz:*bY7 @73f )Ζ'ڶw%c0$48'@~lNuDg((4]N!R6t$zx+["-pr>F\dW'T 8HlA 4ܐ2 bnqWخHB2ut;VP}?/ vL,3="cYk4~$]ͥGΤS)"3j_G6`GL|7+eR2 xDY9lv&sQ?JZJpC)MyRD\=m!fɶ&W;w".<_l؃Z%m aE0ʀ,/yc%I-IG!Ȣ;m)scpAkqTrz׬̛>i3B#i{g?.zWZ+H$nΠ] Bzi[n2P18d}A#&d!!9e=H:}u;nK=#K4u$Qʏq@Î#`_0Zۚ EIN6L* [T"0>0ɑhFbhvO< fPF;Sq܇)O,4X#FY,յ5 5?ZtjעZfzgY!t}b# }17KmhnF᳉Q_N(|x9 dFT೨K}a̛<o&>7N4r)(K숟#($:/$Ah7]Y_ 6TJƨ09kMr MU]@jUqe cy;BebAyUQY$/ηbf0@_ټۓoNq=♉kBACC1U@;U 3y(Fi!WvGxsܼi]Y (:0GLRGiT<]OIl-'nѩp0w"(&E@-1TXUj:IG{0-  _Z@x|P|z"c'A&_U) Տ(uf_﫳Q(ta)|;"IJ4^Q)&_"Ӄc@}UWyNL'j*SLu%ԍň3TlZ;US2_2s"\i!؟+7/\W(&YQH({ A_Ss";bFG4MRw: u9on|ے(CHLkf6?p%ѴtM|ejFs1Czvcp>sg)F?돪k4Ricٛ&ADO}lK]T?@#%.xoneo6  I"Ͼ3ڈUPH̒v%@ jfv)vl:Fb34}! [P p|IM-Y3ο'9kcq.FwjM "zY\}lZ$¬Y-բ;Ɓp*IZaWnv8I;/M<%uG0W.̓7ܕTx)7W{"O|nV9~hc-ęb㭦.[ UB@*D4*Ko30+"v2f 1 P>yu/r&ť /'+^)vqRj|]皳J*3_Lvtxg)c#)Ã|r6otz dGi52HȊ%N`_̀ 4MLxva0]a5@b&B⼿b="<$U>5:iތ?ph 4줰huΤRWX@d3G+ែΐA KQ1KbU~%.,TN8Y2O5BW`4md@ejT J sS{FL Z\V,*cuJ}iꘓznk߉0YD闠/q|4FmsVmEi&Q\tEhfHH bR7 ~jj[䮼 _^\JYBdM"0%'~M} rԪH;?ރoʈV6u4y$l;mۉV#eBWƈ#>uJkpڢ3LyqD  7 duԄ4͋UO!P^sZWOltP26P[l(0bs$KhS<Cdhˏ`s|v*|t+|u{Z oFRi"_LK㣩)MZcCV Ws8)Cc"BEdH] ]VK}q! s $ _ u"s2V!gTW^sS%@bdVDbiWO _ㄔBZ !9T|鳗hsB7Q(-)/t:Н_JAmgO['LY'CwuF[4]F [%qKvKwR[KGgm u7w>`H!,\%}mٟ/'Q֝ޯoޞe޹3x<_X]axCFփA'4@h ꕄjWUɄ^u`b#M冏aÑ*\|cK6r7{a1$6dZ;w9u. /rDLfC]) bk Z"*z#)翋oh(]C$^֐q(lz8`]?y. 넋!A'eL%|!jdx nCma?; gd~U˴O`y$l g ?-iNu{B_]!6d/qn@ 8~Gyo?Hm+>N?,uدآa`G7+_'>u@0!U҄d0`nJI#'~ʤ^[x*E^e$>L"Y%$=( ,_CU}MKO.xH`τ4fZSBD_TthR@ |~LF,d{ ?sU/ h%LUDp](}h(tHX]]ӗ^ 'GM#N˻.5iU倮 K M-`ݼ̈<"ְp*T F*Bb}K1i1r9M O]40RĦKJ`͍ǏGݴPQ Md``Ts|2_BV`h~YhǼ/BI`דT*&-='X8P4˕3Ls|i)[]!{Gmv} yBTPQĵ-DUX45!8WnʲlUzvL5ʼnSAX؅47K6utVw}5k e l^)%=tB\b5R({afǜ}iYVy cy0T`z7/A6oG00뢓HU|51\:쯈;cyp09\"ܚ%JH׿+AH$<\M1 *&Fеopݦ}-0՜e nfg0A& خVҚur%"#=!=6TDa5%sZFFicz0h!EUb6*^ :o:h6Ȫ륍-y%D<#"kTPMیrG5[n?`6oI Q纎b鞹w" XikQ9{RPq<87.=*.z}o5 !1a0_\rLRղ.&8T[g)Qh¾1_6ae -w142'zCW825INEÐ3Y2cy|?z֎;Uߦlg]z\G+:?A0 $ F!wݚf,Ϣ[ç]N]$e5 0HΧ٠S&V хS䊊ӘˎhY}++^{S qۜ)@LЇ JK^+ 79߯y-~,sڢp)R6A  !{4i֧¨ NC"iBoK**ΉZ y\mYb+$ ru&W}ͫ%T?0gApО=ǟuc2$6ۄ‰ OG;,7jzBOবʝ:ƃߡYb!/DN❤fըcpׂ 7E~v|r0{ԍRkSK9W lS~0C,;ɔ=pe6rj pC%H16 &7B$BSRM㣶6s_EG4Smƍ}ĥh *5iOC揕sW+)ks 1E??c bĬH1tJ_.QR]ADYrOcwBQ'dL#=,yvzxtw{d*j9 1vW|fKYZvwDA!dĤۥÃR|uOMiORG} َ0Nf4k^&ll7 N8ta )FuJ\o_1o0y%ErxN 6r0r&giG!V$n07Q6QNj'iq]"t S9=3FΙ:cixi/1.={>,! Sad4L/b/S\>J5bq5el%lrW<AR%ܝ$HDHŪc҄6 rC,*l䑜P19A IlNV<0bv cT.Ip)fCV+17,zd lGl |cDIr_ M$nŌ(H9s<&,/(K/OjH !/O;k=^Gx6 6F,zydKZӛ-&#&yzʐ=V<>Ԙvmi3/B${Z& Pa͙Tv/ Vƾ8br 4 j"~Ru”QHp͊;НRlX*{.SѵZ^ lzp\Q~d+,(+뷫)O]\{B~D^22DصI+h^jGVW$(U)LF mK* N=5 _'`Sǖ/g1(d[j=B~BYejF@dsSJVC ^Az忆oB9{:h4尼m֢jL(˳d&w րɮ$^ϰh^6kA#6baZed '!&r7q<%$Qe8x_?cۮ_ XyP_xwIgwXV2TgP'*Ŋü!wa۞\.Yst8or\ԖG*!CH %lN{3$Nn:, K^>LLĕT~`Owqt`/.~WZu*XSdɤ.yD^6O"[. 3zDq HǢs Kv!䒲H,0m'UL7z|FE ZI]!gS40Ar“H}3ݙ+XKђI-vn0Q @J,WKzEc7Y7а5lV*ߘn f\(‡X4?YZla1>zI{ĉzV(%v'2sޠ_jYcOx@{csMi+c.~?vٹ|Є@j{ ckkOwӅZOwn-@>y:s|5ZpX}n"8'IÏvu}Jǝq4 +aLDժRA"ϖ  p~yi5P_q2*=YݙeOo{IQ@&b}&ϕ"G3B3_7DDž9vBծ%^C-/G %oov&;o>ԑDos.#|OiiuU`&PUucJ ̜uPq؞*"hQZ ,\jza-͊]3&seb#Ln=A,˫Z{zԿt|'FAmn0GHc&%6v-zͿuۓĩPi hVЈb诂a]R3ݎ .~w 7Zk18xBD6ъ*PqL<%1+t?Vr';Yok=&̀+W++?ߝgi#Jq8BCXb׋P5̲2e̟s]Pqf),o4MM[_zt1f0xE?9" *h[fmYQCkQD-I]-izS:Q,,PY{9aTاu?i8ܶ3xnu&. $-~f{s}OnefǷ=c uir=7~K}lu)u̍f?~wPn ֨rDT*rƧu]/nz|m&\>KˆMoKV9bmr=ۖϟ_ݘA2E^~Hc h ب^kX{=dUv)2iK1gj Y}_[BRJ.12Gaǻuof͡(=`}OaT@F:D \X}7/͒COuqʚ-al^"'ݞc}~ (L 4:Rfufd]2gTl)-3L6 gT{@6]F8 zIZ 9uZr66*)%,j QGY?| 7y*6>vsuu =>q;koSBbʏ~`a>:" L3:|3GB#ۋS_>B 6|U0TK h?B(еO{|XSzM?Dj[L꿐c8[!{ʕN&0uĿ=F}{7]9؎:i>"=khoSf8n8'y`Hs<{.٠a^yö#1{Bo>}ȝ, od6ߓ0YوxKWxrV.m#]r#aq{O!ܦJ6NmN#siiE+; vFD0۳|Ωb xff覬p+ ]$ ?&3DKN029UG&,))c`x5Gfp0Prl?ƸSߖ^vmpξ3Eޕ*A/yJDE^w|P^Zlt)G݃alHbBs7\dmJ#Y}Z)_3gNq*l@ 6Ʃ77kW؜MВQ=3y28vh;GY@!a"q߂<IVLV]>>ZtElD VU^XNX&v]EPhfE%\-5Z A UM 3MgfGmNt1/u6]篍n~]xX} 0 ,8:p9m%!B.t{kUn t%}^ ^53kThAb:{Xh 2 _! P5+nyj׸S R "FMv&ä %Ajw3|X=T>O3"Hb?tulz"s0웼2+4+:RD ,PFvwe=;%G'7<QPx Nܴw:I9h~뜾yLZ{˘>Kh}SkmvE\Z]>:q#O_l|MI<NݘۺG:֣gVԔqbZO7bp3lcƯ9tv- Y7/A OqrVw Mw*\}NDE7#sZO[**vTîhZ~MୢXѪp {OAW EvUKM,c`oӪ;2sj`ͮo3?潍FTCR;,em1'lG`P2/1hA;o$2p}}}}Oڂ@Iȩ V+~֪ F^>(ɶֳ탽^}U$̭,GU[JF ƃw בZC;Ķ{j=-&Jw[̟G;.j|bn>AJ.E^MlΪ#g]O~g; R%S|ewIEOkभa x2pau.p=@Zz; Uko͓%Ӈ6u6UI䭓5LyHL2 rtgȤ<( !%'Fu\tܮb7wY"V VK1֜퀖es\hiM[&V1Vt[V"(p5u?[@ fk՗.ԩhv%yC'ُ}_ΔM{&?闐ŬhG ^a H:lpVjf>4吂G4#K ?H'p+|Q)<7T+F4YU9~YZP\]ƀ $[n X݂ ~w眙skRU]zyOmz}~'0Z|FhzS=*_ a˸nkkwm'iyckPY(T3MMEɯ><<[NˠFns :Ρe ‘ QY$<I>Aʓ 5Oh=R0bѩKXۇ%^t.`"V~ES0_ViO; p?SLK`QF[K-ٚ-jFi66#V[m!e  !Ͻ  qeEeM  (1Dy#J|v/)`xaߢ{;K+'9Loe42#n1Qm S!Ɋ]04!7GE~z 90 :5^cdmwlSӽ WNI!H:At>]%XFɦsW=cb qtA.+ho(|( RW-Ie4`6l⋓ K5͙W3 9Y˲SӬ^%wcm73X_ R03z"=KG_U w$肺A%6A  #/;:;g]["e5y~)U+1R@L\Ȅx{~t<Ɏ7&f_guZpVMKgMeLj~f/}*5@eU3ӥT\l:٧@scBZ%"?O!ZğLC7&ZS?dwx~VvTT+M%3<jn*Z0ނYsBLRM h/͛(5L"᳜vulԖELVj̏=_W$V* ^zDgj}f$v"2o3,_/7~:K o uqu(}ȩP]j̿Ӡæ܊a+Df\ێl, I|lTy9+4* O +k-N+Jq=GlLrܝ\J:NPO|⼒tdc *(-2L^U}lk\K0?69g=deǦĔc]>v3 p/?<qv3,*)-DA 4BԶ픝'y&~=k(]@.c$S:ub鉛VI /rښgێL?mI 02GfHki*f&bd%)/Kf`V؃xTQ]V-lNfI[|:nkRō[XKOt,D /s-%!\aIxJɼC˽{xG|Y!~`w<Ԫs;7My~2{cnhJ3L$m+oLy)NATZ(C |&'b:Ai|_ȡ!#z*Q-t?:XltM>[zyRt0͹!j[*LŵbWRՓ*zJ ٜ&O(> WvU޿!_+;HaMw0iO^OHz{䖬v<"9W󹀻0FF͗q>3x[y1@g HrӉe&U+s>`}fޖb IHWe?WO3oT H]4>ʻPkipP|ByzJ[dJ0br֋n8TA1b4)[! f&nS_QӨy޳qQ,aVUmsG#O*l)FJԅ%g% 3+͜ t۬7GVɫ%K".(oka*Vu-94c׭͞j$gچOOLs &FAw ]7@'BѸ&UYJ))A]v:#xhE^x>r"u13O==XeG 0p,cfZI\s&GNF0:>HDG{ Ԇ6^Z̭,Wʛ-`Pjj5jqyu7rj!6S|I"zr0IeݒȠBpRyиg܅O 6T~CͧUsvR{6iu0[z=!ͧoLAcfxbJZGC(2: s\{+r}\Rn^IBcSq7S9?yfAy9" L{rSA#6r93`c "[;\8> 4^ue.7ޙev\uuvKj=;=(!$:aj|ʗ'P :obZD勏V5s*-皕\,f"3%KSxm:-[l+BvE]kBOMY2[B4)B.O qQ?QQ=J01bƪ:/ҟ|o-#F3x#oi0|* /'<{N,tv<uZĐF#eeFl~51?a^=!Lʥs/!zR+e(p/e2G.ހaeRV+\ 4}]&Ͳ;2ɚr8Yn gy,F뮒PO9(<[?$ ˢߠnY3]*&ǰZB陼Eҋ6\p4EӦT@;UgL5TQs?E{ ({K^Q(L8R8c3RJ#՗oN[hZîXY5 ;^D=|/p_{qJOI"fm^:JSf7ۿY$ܰg:IÃL'0<#/|ܿY?lGo^7¡*[)ԓ[{Ya2Eݕ`2Ea!jvԈ]SK)ڕ?7;fvoF;K $wx)WʭA[+l%-xDCmIS9˨AxVUlc[ԊĎw\vo 7cW?@QQ0;u|bn΂q8pr?VN;4%S>3q E$xa5s(;suؐOZ)d 9D>RfZ4h'+7^c/4|@[v(vw,)WR=fIX>f9.xq.porWpp/~wOƋS~=8?w_=M$&*M' zWl6BwF{CR+_J:BgnJ--QxhWY;<} pi5 VS6e'>8U3ʠ3@FҪ ߘ%.ZxWSaBNhQ{ =eq*O?w*( 6M笣L7.ɣ_4ơJ6M~Qlpc /wB;>d 9NMÏssrϻ^mҳ&ƒ8\GE0ȍZѹ|c6L?+&GCsF<"+؏$ojYL4wi#W3jB6̈s_AsiAi71"و1t2&z j/BQ2p1j޺_W浡oP,|ȩE8`>-gdaLJ`9P#>m&\.{B~UZWdI%W<5.xiG3ؘ9)#z#Q:+=wn֘׫o"hͨǯ':LE4ɜIUtò"F럜_l__m%$%+"+ NlŰSrDŁtx1z޹D2`(6)O D,O6L}!Jϰ&ggR/ "rҿYY \9CUm͔R"^")wTZV)ie1,y_fsϲ3*,ocgU=\)2NRz^?ualAEZ'Ҹ|Q|7E nc@+snۖ"ZB0T/m W08) hWZVWGpGTd9|-sk4cz1s"5%Z<;J pD%(!\ú![*oE @+\xM/Eۦ@pǮo3Do!n?e٫GAx1?kWн}A%>W{ [tz}ɪbM^hi/+~CXLW\G=-ظpO%;=v+NۅE+z(7suسW$?!F|f~6শ#^ {.@b4t e]Ŵ8R SߖS?v1-rܕ܉iaCZNuڐlIšB4$Dʤ7VYKmP% 8FO7F|K $Q;X2m?aO4?G_l|KSN"{n~pϡ_{ )h?ZO̥H}yo݃jWKP_AѧN~4wl58J(DfwJ8B~s@v }ܴi|S8uM7Zڪuvo2%ty0~9rZH@𓏞OA|m!2x)㯞7`%5 wT~Jɴt ie/ۣN&^/ױ^8۬?]WS)6$#.Xc/]ߑd8}爣d ڈ` '@۩a)}#6e}mGҏ lr{zbY n[J0!)vQ6Cdupjm.F`-(DĪwW-OEv\xn#wGZQ;6fNlTS:c8NY{R]3< kV&KF|m{4'O,3.ikxr:exݤM@Wѫ9bHōjU?M*dsX+]g10_9sP92}~45!zު*([39,%?5{<<25opVAʊi98!]xRw{M}ٶt\6xbx܍O7&t=܏O{~;<41čFN'$إNb\v p/+AN>Awכ)~eЏx \Z~877t]j]oSu8?Mx?wop{)?%VL/[7[gNVkG9)YG9XMol{kNwBKQ{Z}fE&J:iV z fX$B2/Hiky ܇jRY(`ė9n nu7 7hf}ek;Q灕<Su\-O˜SBۣx@b=3׷bqz>ПS.ȭXUsT{bFI #Pjw%GI ~xkuķK=Ub^fwlyAS,^t,Jf lAv1NT银O `u`$+#@2ȒgX~cpg6NloGɪoFD_|ÿ(f\˯94) &b] 毫5k-GPBY׭ 6&Vi"#+u|gf0]XE2#n(ybǀ.,n0' <vR><}HtcrB&m:8o|O9_FpCvuNg )E;9?ݟ,p[y|?RwfZE3Z"p)rv6 %ؘ;miCB}7\>xmYg]<9֖bLֹ/=珕$3 =.I̺,E=K+9Vz0*e}m#sgBz_@LB1Ҝdtt@t==A!y+A{KҸ&$nUM|{QM]ajkbA dk>"R-/ec!=Qaac#חoz<z#V4[8#k 7r]eP?1DNt3*\ڃ[DH"bb|N5!k Mk[k8hOi$ĕ"Qev džvFDύíO ͊ٙxSPN~< 8]7i[c3<|W$rfˏK 떧!y'ػ ,]?Ծq%,AnU"w]v?[H{?.XTLlFxdAj_:.XgjpA Q);W yflqD#PT"؜T5 +gD=]|8Xb^k<'۽}[VPu'u45ƈ/}MFnfUNz~1|8+ʓo^pBk9G$fswFˌ@-{ K/tiw7w p(^NQep⴫F}lzٴGVƨw6' 5JpDE=(6.]EP#7-xz=]"Qi!KOwW`veЛ2 b$݉I9/@㝃'-Kr ׷tQKtǚ$U|喒Z‚amnEڂ |/Yל 凗[_,3pE1mwwMԡ\w~.kPK$RH-x-ḏH;-j'] w\1I~{FZ' *,$+KqcԎO 5L'97s;i$˟9R:73xj< GKl!t֪eQ~(mbR6i¹\4?^p-iJAԯV|E\Ki_=-٫DŽwpb=(yrj-nB 9x]"# >߳jAD;XXqtӟSݓmou n-\ځ8>[ٚ=]`<1a>fdC0&UoqJCm2C+FǯTe|G鼮 ~7Wg1^crǵͅ%Y.=Y n$:R U}z}X]~6xeh1@VYL (%!Şj JDyMܹB8EOn 2 22KփJ z98DSWy0=ą`چ'kbCa3ЀNiqdޟQkd~yH+WkĨE:Vsn<d-aAV=n!z+_U. "R *!?]DX_ {_pGO\@.̧(F;M~;g@0]Z/֨+VBQaVwcg S`є{ȵ80#>fQ/DxM/[D^1PX=3IaLWl)խPY?%},.l@<}|˥508Жm56e'6>B֚D5Sa77m,gbp'G]/mD0ޒo]#AB[,%8CӂdyG#ZU@phZ>w2}CO,N~u>)YTme8Ϯ"Y*ӐrC~1qʜ@knكꉇSFP7n&W R 1{I;:YXqYGv6g:5Y=B=>_+HWˮR3֥%|c1MWV]_3 z! ~"MZ%%%H7P3t2ak_(8-<}a i<`fix*H\C+ ,gggz:U *fͲrs_S{yo=)k;Y߻)W3JyI|S"FZIBƧCNռyٗK@@`#&8nZ+4hKB;wAnyЕ8FT X]xT=Lvn zɞZιcS#rརt úR|]<[h2i 9ݽ檡wA\į3+;3즭ny6vs'_ߦR=#?N$Ӟ{]U}N1%d| QEw*.? !2e ~j×"cIcoqW@`9X=!~&fUdz@$RCl~aE"LDÄ Lސ퉭O#/ yud+] y1T+&J.#{Q܆qσbUw Bhz,L` TG?c#j}Z)P=ZQ@5Gd949HRF z-F)Xs16 1v]{P1I+ 'Q]tKhIUFD@' ̪_u-R%BPVՐd0r1JOಋfh:= x% TSk_"`6|Tsh MiNt̨wa:uOL\N ܌;kQq~rl0a_Ij]eta gbUt{2G;R:`_N͆2y9lVuoh`y9`RpCcʮVĠ ] Nf3pM8!;UzU<FiY+K!8 _&C ;Iyk*" \?r!o_Cߚsىè]$)WxCTiu VL\E; v/ e7϶Stۘ ?Bz-L^][uQfyu6O9LE32V$qx&A3iti˄/XfCOYhHXKqLx%_2HՈ0<9.` ff7Ǜ5?G&TJDAS" #a7C[;ׂ1-P|ΡἘ\TI˄Q?`]48;gДi.1S˩_ z38bJxkJP19i;86'IJ5A$t~!8IFKϡZ(r~7˗ p}ė+Bp~3E랞x95c}t1A0RiFv Uks (+*p5Mr]w7=Dw.^"n[ 6%V'6taN#[£UnN.ְ uꮑV.7-K5|D#x\_վGd|og>smbĩ. dӍꍟc1QSKTatE&(@muBqF}nEб_*Ԟ̏8B ZMXf:caR%KLm~dDBzw6HsI1h 7c=,|n[ m#*btTڜ~~9 +q{}TKB }Gq:kNQ0@@ҭZԭO!oS%NǛ#yU>獤K]wpCz p]2S/y7].3Ϳ< 9cDj*7,ޤ߶rt] Y毶;P?%^IM|a2A4V;gTLIM0-qx\^9sYJZFh .!ݙŕ_juG0o(e#3!$`s忀7[]aRӏd@"Qk':6tB+sC]ok$)#j\.n ^{eЇ?lKп0l 7 rzMjњ ,5 AI6ӗVA*Z~^_GO+_~#}tBI-d)QkM;&>Ӓ&$4>[n'8{}{\v8am+PqF(W/IKE]aW32جoF:L-Go}J/%*`Ǧi. L\S|3*q;l2T߿oTHn2`xeG e3[Б7U/wh[r[mې}gוէ#J١žI+׋(7r&_lUl_ 2bs_:X+Ffus8G+3۟~7!kj/7 {# ~1 B|lI2}ZFDFFs%97G~(Ya%VJ21>󜷁;}mj+m7lt X0sv_UnZ\ū2n,_{|uy3wUDǻ"ݣ=!iG ,Eބ<>y$FE;*{= da:XZS}H"}#]`?>Č oY늫xo]ͺB<K+OEco$[#Bp6yNe8___^72yſ<]n.7&q-)||kx(VJs᠔˖UWpvR.,xeKDv2sJ ,Hq\MRHi!dvϑFn8 MSZSи#)rp?Wq~! GdMcHr޹!|NSN( &Ϸ".y+ޱTHvRsi(y6Q%_Gܟ15MWk)K]30V|ҝMB. K͘ ʧŹ x-uy' G? ڧ&P%Sgdzf+rCE[R!ڍ{?a,x88Gy!y7c)%[Ul_-D$Rg :M2)]x?ҔZJ8_4d"b4IcU_I)ۅViQh5$L,n_^ HH=mբ9Ih?qufI>!h*P)d UeygC >y@AoG5$W≢Ϻ}m%Rft緄IUKzOfU{~5jfNX Ip$\C>khߴ 0!]-o:CUbވfl!aY| &\$`SRFC@"0 R+۞YX])OѻgHS% خO,ǽ?v.:huiPXNx{;!hP-RƦaqՔo^h~R, 鳰{B\,_7ߋԚ{}\E>tFӷX_aXyeseQƫ& ;ȃܣKihwô{X!l~ C>F^?+j>㶾=j!Bݏ"msubk7]By=, <;d+=3s/M,ꡈad_st1˴̅Z. IN5> "]7bub'MAƸvwE~/`5Ӫ - Tc9ꂷN-*&f,yP<ԅrG"FNO!5GVЏɬ$ɠ7 'W%,/( Owol|vO\X oEWG)yxDEFy9D>k+ 3\;8uIƖeU3Uf`2 ҧ\sN_qc"LݗF[+yοqJ%N͢=N-5Pwb ;c]fuFљٮҎ3UEMA#'QaUUmٸBg痂tLbwws,L3}sYEWVum%ݥ>^;YukLt<ŀ9-1cTcь摒/ۼO>({ Hϒ5?vz|;d#.Uf%ksj"[myn<6)F/zlSe,tp0]5` d``cw0XJ"%ZMAw_o4+ah:&X/ QwyK17jiׅgpFL~KعX h&E3GqȂ]÷{6EKax`:wGܸEZUf-k*..C>k..S[ TY<)FVXKlhЮW'UTYҐ#)U0S7޼z;od5 ׍. i{2ȓP+a&".TY.cXDE t!0Q+ܿ7= _-Pf2VotӅ6UXw)xFifϷd6Uadfǖ0>w3l=Tm:}OFU%/WXDeHh%ũ>hSѦj:^^ŨL ӳ,?22łk30e(b>(v$擁/Ԛ5NCބz`dD<.0w:I`/־ I?jjV ߂PXZ(:}XxzQ) ܗu_kEF:8 O V_־n̈́D#xS{Ԡi S 8C\]ϫVqM{C$\]o>B pqӶU[Y[Lm>.I~O;`QA+dUNۢgAhkpߌ. I1< ,"Ա Cf'JaƠ_E7BqFtotC, ҫ$R#/J d1 uࢸAdۘkcWӾU5gbɢk M-cj'Kܩ6DYڟ+=QQ>NK\hb /Ns 6\+n 4#r`歁HcXxE=РtˇJ˼P=zg:%ɑ賭I>gJͷ=3񴟗OOWta7tPworQ^"W$::njZY~T>U;2 ~m< yca{\M~P qYW?#$͵-7=ZIڋ:\];,b^}]5$ rGdQ)W֢ǫ婗u.iz9~$=Bb h1\ ݷftMʯܘ &޻xlkªo߂y>ÄbQ '鲼w'}[XG';p{t) !l̅neS0ծ&B5[uCW̠OqZғ y#+ :;ewzf58}4X+Bv($|$Yh(Z6PAtd/ N:Jq: khcͶQ$Rp<|ڏw4m>o׹ZwS5(;=mi}W+UVb' _聡EJN^s󷋷~ Pz.@v `p 5k/NmLO[g?.*IH2xQ">y[E9^wsA> Ю7 ,ChfًEŞ|E<4}2ԧ``J^o>Nn|UoX5Ue8%AۑkJ1tX䣣+@2UPs5GE꙰C(|7iZt9]빳#xg ]r؅ix0d?jU)b;N>5^Vf4P;r)ذv E"=T!udKuͥHa倏zy}& 58iwfZ[Z\w]**+9O; 4ϫBO&t7h=A|[i#3)qLSUd9t0132Y m$Kf?H|ٵ|0r!]115aY~1Cͨ6nl>F@4@`qL$=GVAa[8IS4HvR)pEZ1NiݳMߴ+y<#>j\T}V1U#JY4Y $ԧLQV+vc8:74m0w3Ì5X{2aޢSC@퉓;5UaW)( @zF2ddB&JX!8adXk<3G֏qNڿY~̻0vZ :'O}ְpDj* 퓣1T CP @j+BX$k2{> 4>b" M0\7h&ZrW5xy^qd_ن&}I9!r6%~+UM6)?bn٧<Q@v;:ճ!í>Q=Sw^ަ&@ְ\Um <|J3匶%vGY+8GX'[Ą$IҜ)v%T1ob[RޖO}zM]74K,G\ZBAIܟšTF~gV'잏8Vd7u:ș1fÔ`ػ+!Yc:ƛghNg'p\G*QQwF/^ tv`| ql[̹n zI. R%PIߵIYCYPy UH PE$A9ˣ{icV:]l2/ jPe4|Y׾dE$c ېgBjh:[Sz'xxճ4}E^%.[۪Ȟ (X 9UHtv?Ú5##g-kgh 6HcK*Ç_NJ4c'1!\Gm31szd`PT7X[LYaHVkBC}Om!hbw0w;Y"JpDb_T=Kܦ5zB @q~>Tʔ*GSoNϩ_]TG܇$#|ژ`PE󇐻]y\-jZ_tA{~^Qa,[UV xڮJd2Mu9dEҜ|Aݾc^&g_ xx)8LcLV$(cyo^mfЊgx[蜈Eը̷LL ي XiXK<B)fmUm[ʡ Ս_R֝~7eڒguD$Z>E4H1|t[RѯweQQA,kodBRٍuk]6ҜHrd&)! @1h4KqpnM՟qxZɋ^K'`BQ~]sOc{Fg(X~=ӡRP9Jk? vzv^XEk1Jʖ53\ܞ(R:ODƁKJ֐++LtL{<ʃ5[)jLWJ¹aBNs9vg ߒ_9]'7{2R=)\}teVaWsS-U䒏-(KjS4T0J=?1Mm8F'q W6 UWw4.ځĞ\$%^!M} N_HmP)+NR}ݱl w !QvB ok懑9Ϸ[ɬE$Lէ bezB--7ZJ?C9r -`:9wŷp+%yj11A>7h-"T} ɮbUQXrpgB45djnBBU85s >9}S/;XyU|m\ڽŤ: ̜,C}?_yww3:=%uOV/^w%K>Gbx;[;\k7};}pVcaq^e .LD70؈eVVB^mLqQkltpVELu/0۝N[Ab6Z;b; e. LqvD辴}˙کBi <,Kg>;̗>g+x UHfLhE|m9ol<0W3I>SDKǣG[4Hj悇~BEn4Wۗ p6ur wOa˨E܆9ޡr;9FC!0UR#q"(4lc%CpE4H=;ǓCev~2f|)5's*B}[qhy&ߊj8Y)LO 橼`D>d[W6?Xݖ, wkS#PHM)b5,<^Ϗ~+p0!SLz pE)&Nu!*I.H7/JZOS)"&&:P< $oi\]pƂBM1ErWj_<ybhp*F~ăߩ%zLq_{өw>.D ]qojY$dd$auGaw,Kp0PJգjV Cx.깗.wϱ@\Yi qyŷer?L74dqD"cT&t:e*}5XyڃTfiۦ{ ZqqNS̥qX_[\:@@G Pt?R[Zc.q]S0nB\xfy̺N'#DKodkyշq3jCV! pqŕCjIrB.k+n kUtVպoP:t8YC _V8'za$oYuܶC %9੊*mVȇ?g1v"ܨT_Wٽ)Aq*fIDD43̸,W؎[53, B pmzHHʡu + }"oA4~uJ\V^gURqѹfss,}g5EAd]Oo >UF: P1rDҌDCǴ=dt> >޾"Tc*!be4E8dl',MQ~N2Ȼ6LM,w+ Lo4T,)3bi?@YԪe͹ 7HZ7HHHڟkv>¥>w>vԳ\XF)l䕄g&Ww2g蔧*"IHtmʌ/oհ<Ա)c2׾82\R*҉"2{=S`Oub I r`?ٶ (y%3#!<5j8~uԳ*fGIZtUzoU}|},jxT=@KgIl!4ÕVO.N*Bc?Wg%GRk?~Tt1+Y?>Oo.ήMgfSFvgcy310<.bw^ ߑ@v[\pbO0,t_'NPzcm bK2֚|lYG S1/"&N6 )!#npQXoԃl`mVbʰ +ZYI&Y8Ӄ;(3W&" +yk6KJ@0M*AZk:*Hr>4v@ZMVȿVZ JswJY3$߷8hR毚,:ӌuj~?D[0SNm[.oy" 37cԠ<$FF/H[/Pm|)BT9H]ȝ؎w`6+qjasy~Cn1ʁal8WoK^k+n}mg;/A%N=NGv6X3W˥s&;5cpVu3%7/f"/lkWVs;%s?gaO ާumHZO[n}VE[80x~I\0BVem>C-VQ]Ԉ _:Wuٯz_Uޗ d{q0 m_u9S:V: D,{6 y}<n^NA]d(۩GE/< tܒ h/4GyOPwxۘgJ}ϫ>l^h`}"  ѠO*P* Rw=y.Y66 ?v3?R1]-o3/<]CN, `ӱphHr/ " >04P9Y"!(92GQg6R1T|` +S, &m OL?wpZm;jR&҂oP}[K>N{B`X8 ::MO>C5P<` L\&]Sv삩@K\ bMa zP/\@0\V=YX{WV:~s!CM&]E MmF゚ @ 'R 8j%\rG2 7D+9&×=nEmeڂBlpCKZ66mY 7mc9NÜX{_p+0YP[9fog-lK~~r-zczwb|]l6" yՇCQpQ JzIiqiSUU؇]{b ,Sq>+3],hAa/<7HشN!]֩*DJSdT-vE|)ZeUv=0ZJvDǧnht#ehXv7VN7L(dָֻvO \H$1`h9B?fg[CffO?)n@#E:u؈f%2zGMΜn1ts|n4-C%n9:"n^73+#W[^\B3cppin  cIЊІlɪYR^;YghsPLio<0H@?!ӝs#*xqmEBqpHZgko8q\$f&59# v.t.21wﺌsNy.zzjzTOem{uHX%nNUr=_ŝ*#R/qݸ3}P-O@lUFk>,S3T91,m L\CS!Q53xˠ }!hxFW^6n*OzsdqyچFDxʳ少.c^W;Ñ7b ۴S'+0\^]65,l?TAզk;`Nv@ [.Ok=fbTDvcsQXj7kXvG'1Ɨ CУȸp'Û=ޛnS,!MoOöGLbdJZCp݊v4AN:D]4S53tM+_s["/꣟(Aa>1M, vG##9"h֝GLd$KHPo"' =۬@㴏+3~9" /6ijsy^ ,ʢ$jp&h6*&3Krʓ}ɦMȨiORDIjrO^bݟ`Wx5 l e)/j 0l.5fM.XfrR*`JW1fL?#[2ܻ;թv b믐<tXp DG0ǙD>"@.B[JpUc K5}PP;^e[") ^(sXj =_) ̽FTi]ӯ Ke1틼1%LM^dO >By* O]?x)7ˈ1e^;<8Xuͭ'w-FF\ #HS9@TIou qjߐx;TT؊6qip$ dn9[43ޞ-(.h\rFmɯj[7iA9VN- x|xoiQeYYEG]mUTYZXfOUu$\n= M @>qDJiб>)i˧& kӳG_>v'YⳌY_W}WM|+F侟poY F<?E.R縟\ko.B؟stZɂ DlCqgҕ,_|u=ݓwƝq.됾x=2xနD(LYKCnf3̉1<Ϭ.B쟔ECQr`fzNc]H͑]}NV PL(^9L2 mgAx1ߙ '44%9ʳ|~?-rĎ7w|pPv;aZ -6"yUC{͟FE|(If= Qё$>F._(T]*S@G9׮PZ3J!#ƨ%O?2g +5~CIz|hwWZy"5t^]nYSOUs of\'ic[.S(+ $`ZC?h#99VGDy;#ʾZTٳqL.Fd^(tMq!k޵˰j^3'0'7Y9/G@!e W/ UX~yǵ=ŽPW2؄ڻ*9 U'+<9Y4lQB*zaPeQuRlD?TWrG1MG!?.F:kw:[_nm'j4CZzzgkXXXLY%i?O6ե$.H,CA5I^7=sx;g}F͔Nj˰m v6TrC[w[ +vw2:lj~|l- ÓSAZE;InEڴ)p,al(YUgqZlR ds{B+ldq>eb4?紈ru.ƹ AbDs5΄O9^}| ?xu:qNߤ(?x r"ًxyu!=ȯ45lVp] >'~uM6[G۫I7x|d({FBɪ^rF*AGp2Ji#y}q)7YVU#􃠙3q^Hl}P&*/;keOJ\|L`m?d@$ȟaqbwMgUu[EM+V֓ITY{t|Fc*GhZX2Ll[X3N)tjd6-6>][(Syq09kܪQ)9d/G~ԡQújrgw`/*38[G)<)0cd=,- *[:tjWɹ8,[l HnX_pZqmo6)xVh]`O–bRSqdΓ_c'Э#/iMBG5L55Nt&]m7?,qZ|V[?*Y>F o:Is",,oEj-+M PΟ5#5!Xl=_J(_hH VHp;2N3llwZ>i9zB Rdo&hc/.1-C'Ep0p[lRt-ҲRBu\$EY,yVyۙoϭH_VEס8#҇T<ڞnҶ',u'watX/S!9JJ׹P5=>ltbmG oڎkekǐe{Mq5G]+vWq<щgv`MkfEEU}ځr)`R:<&Ж)眼u^*7<ѡhM sT_\6'3O!,bdc"{zL*!h\#νÔWXz| l syl'$:5YbLnלfX{=[PMu@ɶHs  Ё}sC['_i SX;>nssnMj;4İ+ ]ع҅m]h2[C'&h}`ͺi~]1uh:?G)68$οz5#5g^~=ug[vm5}X fsK[8ZL}:/eM_GYmv#ͭ6 a QRѪ٬[SAwOB/Jaj# 4B;Ů4$}3 m/8k/~lOX+j?aϥgnSQ͓#Q?ڞX9]튇@[G.x?{Wbӛن9 *ֿr2e7lW@5ГV_>^:S|Ok8*<|9*Y| ; ĞbR#-ٸvu'Ec̜2|tfQLpNL~]P#d$|//mŭAZl|(MUi +BbWR=iNf<}Ec'Q!|믠ɭc[^uO7n59bju%OB51ĿSn/$Hm6`˾m!\Ra^i*/Qz9YS#no]ʑ["'VSe$0+z>eZ/GG)~_ҎWQ;jr?oRg7jW %&|Vc;ѽwV>;:\ GjS~zܒ q$ΚyØq@ U~rͤ<\bA-,Z{oj1f@ag\֬,@-Qk:-[O Om^'Ljp]1֨a„3ߐp~8]#Y=Y>ȶPSAq 8RD_R<x@(·)bW;7p+).Ul1 ̋F{Xbs)pNgri\D*h _\=cv<8h(͓!I`I.X3ֿ`-gfyU.ך^7O!sJi,85 YJ rߤ~񼝊f$s +L$w [(tlAB~#7-X̮q0&LaxLAqR]E-b_m+zM}%*wArmF_*k=Usp4֒BҘ0vv=0rd*Rf&@IOˇIS9Cas}Xs`śgTZLĤҥgdHsǞ)=#lƯm 2]:]ڑKE-Q•TCBP$rچ^nm;1e!1:n&]il-? ,ɛ@#S'9ak2[_RC,V~ӆaM-j'c9s*6SR29-.Fǯ9aL[ؤ 994ǪFG$O12hEq޵Qh-E2#XXz׊.FZ'g)䎡x%޷ՐC-\7WU%d^I " 7 CڷEqDw=>ndQf{%zIm@ ny{`f>.Ͱ #%ܐ7}2$Zy)ian9bCP(z46(r:NO"jJPm[:;|t p@([?FŔӔZb!p68z 0y AfQăy1D~>a^ _T?(gwn0InUSdBRO.ӠJ؎*G o6&'$c|VFk vE KE3]tUX kGoPP.(2@~5;|ZR Y͂(yI*sC6wҼY4$S1 ۥ+ŕבhXy.y[ѵmS)p,Y!kjn# D_aaf[i 1 IbIJ>~Z IxYg LTo*EFMЉ60jٔۇ)RlƲ%=i3xF.cQ1ܱU!<;&N22&k!i\D S/.:6oAXuI }WCַe/ (<>4v˼C`+;vAu[Pt,jbæU_w) K{#6FnwBU+sV8atN%˕N(8#.hXdF/\ι|FIVHugMncR(R6?)MLKG cz $mZ•;O(TlR_R6:YIҎ{\3.8l:8nZ> 'ŠE!t x6 'KaG󣹋vn7,8T]DWz.\s@N1U13_+腬ҙdnߧ7{8ߧEMLj>O\IP/zf=e_3Y;?,*?ZjOr"mWiq/Qc5꽾vj1xp:ܺݳ#AG.Z+ֳ(ݨ=- Bܽ{bȞ*q3b| :@dA=Gl&{+32GEWZ~9xTX;pWQ>BJM@J&qf\t7:08>ay&<Єv>|&LRrN|{z 1'WO$ʲ,~Y ɗ[Y 2A Cn)vOfJk4uSݒtc}A'ەКD(|x/$v 2F\h?{'?tO0 ҙ(kO,|PS d#}%||AV1~e`3@9! 0\ͥdTQVQlx 0zvP}Jۿ 21G-ظJxN7l@g'XS%XH~2</@%FT؊!!oo#swaԊĆ)5nEz9Qt'$~t00试(ˉЬghX1hߐ)8/ OA"֝,<,g I)F!u=291;gP:~62cdؖcc0 B*HDヾ2=2`(g1[;!8tvx V:qjy\|-WZU%1dˑƒsX w.)6;4zf!۶$) R4J4yQ| Z$n.*:Q2-q'X힔s~֬)-*VēZrKыW b ض>z<1K4_pT린yw7TÖ.t6VQ(;AqL]_fE~[J9^ L}DR?:)o!g>/EU\,y?&\\bgӘWA)b)CA<~bƙ–pB0ɧ׮‚EvC";+۔#NyNGԲ IM%No -\pNIKH|T`qi)As>aoYO2CE~T(W?᪶E)6MBv"z1E`74=/qL TIQ[UY>n@Q;O27N0ڿ/)¿(K;>?DC+*q]@T/4ʅ-m-Z7Uݙ3uݸ&QX;ì󞵾ǩ C)2)/H_A-~&e*#GdLMpp;4&3u"S 1M X M-;͵lG)]񠟴H$6UH$=\ ,} 6g* ݉%p#ܰ@VnC<[ %.ڙ.A ϐʂpoQ!goLD$&b..k< &v/QeufPlh{~߆x sW#RαGskFn(3 @ j*"+Q[# R=¢vuWp Wh;Gc3IAGhDrҌStIQ41J:iz&A*wbh, Թ - <5L+4}ZAp`NNMYkbHQX 3CU1i+FcjX-K|jQl{,T(=o\Q)lፂT4-U[EEljɉ vͲm}L;\_{ [;h-eV؎;ɞ-sֳv3 ؑMNf: uW64=lIm WLuVt/8»:AˁYDN]~,n$9m|a 8v{w8&xaiupTy7yOx_H|YTh“ޭDNWc_阯Za6-h9~!KU0XѫV! ;WZOw{N΃v 9+韤+Pȏ^ݒ 8XƳ\5+ÄAS $IR}?v#hʌw|O U(V+JOI3͌wio`HzCQ njw遡PRh 6͇n˽uz{1d_*FK̯>3v)=) 5z[  ޥuo࿄T? GT K܊3Sy/F]ă'htM&9fHGRN0 ڔaQ$ww312gz5G1Tq->~ͭ& 52k t܄YާZ`yI՜0[+٧]3Uy՜#\egz{Sk,m>mim)eOZxq 멽Ǒv/z1`.{# 97ҟGn3Yc秵?z7lj?j5gG y0!(1-,Vm9\[bW8ƗHuw>'^6Jx|?&y E-ZgSR,nO`"j ıZGݒdanvyX]nP0Epo*{^q,jy3HgXrZE|+Nқbӕx{iB'*K9&ř ~a,;hK4* c. 6+{V W0,)ϒ%1[hy}ۋJUD [V.0X0-akv`;2mEeχ9U'[Pb}O+"֩Hu~X' ^fk^bWͲ(L}[;&D3^[J0^\:9\r,Z FWh]c-B"#;s`g|!eP;e!:mbro~Z̫@>~0cI#L68]Y{^ťʑnh [_swpN e<uH{U\\\7MFzq]*R! #P Tቲ/T!P1H,\o]j X;cA?ĦMلhERsҭθ1 KNL4%j߷y5'(y!J/܈4nMrؘOTmWNb@&`4Cb$\#>(&d5?!2 =ʫ<SՆfನx$MUw 5.pIXd,sՏc 2 q9_!$*<8ȳ2,8\k fN*Be{z`RoD7_>` AUN7!"JS[\1*VY+9=_Ny(L9D,AJUD-5&HT2Uq&Zzf_p=7Cycu*x0!$RWGȎOXd5yw1~;#ݭbm~_H,ohIk.AZq'MlclufHh ɰwi\#Fհ [4JLϚxćӊDK"BlٱNrk+:=&FZG^w'G> 0e :hIiP,(oZl i>#4ڪV[dj=|* Fz00db(suQgݓ0R>pbm<\1|q]a\ !%uU%Ir lH3 X$1US. "@E=I;v¹㽸L&"?G)n&_7@b!YHk"]X!l!w.}< ns6A!sf{nǠqHjiC8u1Ø2C3> 0( >oDza1H[!` s&{*I]]$xuA%3 g.ˬb";^syxKdsiqwԭ݄)d^ixJgEBOq9'oU])?G F:*%/_-[RzG=L ד]+QTqUZ%;f8qb\22t^Q8JM,M)PWNz4 Μ9KN.fb'#hRQ97@O ^j\YvPFBk M>a` p#VGȔ1V=8N i 7 }%w(2h5. Hy1|{wD.Р\Nbrm j1gQY:V\6lE'2]]k[("^Z!'!̺*.܁Gk%{'Ny= 0P/kUɤ+*f ؼx%SzdcP*ssA),q5YNW@ [.k ~}T|fuJo{F(@Li 3!9*;R`bT| Ћ{TLvbgEM½àoWL~gve6n_Eфc-SN]L uCsXmڿk+mq90&z?Vw6Rр$n\{F(R bndr:W"e,qJUԇ4@J8(E(< C1?2R(6g4Y\Zv]4au4[(gKH]!7҉[\\+חK) 6DS%->T OXo@a_- z lVoηM iFڏW/Vã 8)?-\0lks2JގG?g኎'?}`$$SQLP+U +1RJ"zWWge(]~$RFˆ#r Br =^\]pö6޿}vcFz:d5k +arĻ%bb M?Hd$@F„ ݴ`gF #),1hfi>pQ&}kG<:k`@@df׏U?NbQ|pn?a!#GnhgDntBD xg"%'B;hB3dk-;ȳ3ِp{sfLu4j!0Ɇ"P_aQ.lZGv 6(Q'DxU'-˜/5~DE7O,Ip|QrXlĮ}umȍl$j 1#ƥ0j<^>M~:j+"{#=r1e0"QqѤWFC'DQݽ9m:&8UCh P1(KYT#IQ١)q.+Xcxb>Y(*e\'7LʰIYt`]N%IpϢI^'9~d9 Mj^Ex BIv`XXF6r׼EQ8bLœr%Ne1k .Pq_Uxň~ U񏛂2Fce*o.hwZ33T&e3(6l=CGy3WIFSD]3&d:J2q ɊN\7zDwzN\D5ytZbj$Uܢ>1\;2@Z9vUM7Dtͩ~:<wP^ovjIis2OpvƋtg[ m3$bQv0.F}԰f-֧ lۯRz)喻_fWz\޹=xhte `1`0%۱11"oG7?qsFGI;?j.[ ]n揋I@`mXXwk6#y /[lX 1V$cb(&sd,Ѧh&}@9tBG>CδrZ@W{&̍vjC$BuW?J7Hjz![t 'wZt\u ʛExa9I5:s,DHGZrL&dPRMD̐eE/ , KItA}bN*j_ƞ?7RƓ#gVyW[vgl2c}XFVO4, GZ[>?gJ=mG?`aeFq'fMHvD\=э[؃):x)'q< $|*l{ =o \g}Z~~N}lO;˛w/䔖/8}9٘`~<5l[ox+M}+NҚ՛mú_왾|^86,ux=_M~T}-/c'=%n\!ߘί,&/CIi7[-rpy'h^`3vb";jYxP 0M!<]pYcF/4J-+ k i!J=.<|95[YaeR9"%U]#btа$+ 3\Իg\9[$&-' _xG; ? ==n3{\@B-tSqoAlXyz nԛ+UNDl<1k:'՚dz:ד`r|lM.>[i\FINHlQOt#|@yFXVB<)Mc|d'4B7y Jzrݬ'K(Kv0aӔt3RJ੢F;!/N4ATB"ZP`H˞)SĀx# ]'\824]ەn1Y9SU~:4STRLE! #3氫C7? -3 Cݓ72}+c{jx\Z+ΟBq?h߆9I$k~8(Đͩj0T"NJ O6" vtPBZ 5^@t!`c-{w%\IJнbԵ hu۸W*Z6LʩPPiQMI)m#P7m&ka5ͩRfZEBgE/|I9  ף* a$_Ҁ$s05<;{;,RQ1bnș󰱨?:'A$Q%!ŗ]%*(9]3h3)yXH _DS/='8 xAd[uvS=2U*_è^Au;#^ebNw;)@ 8\>>LG-TF&jG0A_DܓJKpUn8L\3[-!9Z6;j 6ug-(t=B_T=GL@5? J1G卋U)lC;ڝ:ouwlYq"5=3줥~׋*h>^5uZ-PJKUSW69z>:@G 01<^W.ӛ8j ʩ}\bBR6\Sh }1,yKysō7d^[`+0V蒵^NyKhO nq b쩚S͹6Smj<2aχ.ZSvFc~J<_׾*GB=MNMķ) ÿq35@@q N xZ˪;s&:}b*( b1 E(g- > hfbʞZH\^ RY*^WT[-%m&(Y"d.#% @ XDw wVes KKCJGH2GH_Vx%8tٝ5>:A(V5>YqnIzЍG>I+ *'C\j]ޞ^{CԹet7`Iry- /U52}L*!=i A+tW3/F (3rJoˡwbAŧ ImH믜i0 0u<)$_ )QRLa펶-$S,q:* "!qhsIjZ<|TӱӕR"v hns:}֞2ՓuuZ.mBO 6(v{82CLAѬ?9Ba.=8^STỲOԜ DsJlhn?9To0ǿ%՜4ќ^r|>^J#uf/b+"OONgX?Ky?R|V~|4v=-Q՘[KD */`hqo&p8v.-l{jh L^T_;# Μk*$ Ztם!ˡETUݪb IAB_z)h}-J'q+('qаB#/&^2}-OyUlTo In0=v, aKvջxm65Т4 R?bW`ǣk;!nyncn襬-锏VCQ=n45dG42r&/>hL[jd;G,:f-hLS8m,ZgULFz.)dgaj.:ɪTO}lt}{c^s&SZS2QMNtTؾد{HD8+õzH9 K/x?]C؇bl:ӑӑXvZbĐzxZX U`ڴXrvd;<9 S@MR6=5F|`h:yx֙R s9ͤ`duˎ9V^90g~]5"46u7kNvf8uvڝafs+d1,k)3#^Aԁ>6qwHL#Ru5oyȥ &V\ ʳ|pͫ %Z02 ~H(!8]BxwݛtR%3 |[*vr72Ǥ{Eϗ7\_IHh3Ҙu 5}W68@7_* P3yWqQ!ȣ>)"e he*ڐ~l~@ޝԓ":e͔6=ZR3K~9MR?B:nkݱLAIYXFw%o@~őY6x.8q4[o3a Aw(_* "23Mt /Ko5Y1Ņf,¨ ;w3,Lya #. ?TZ4_hEC6Zʐ6K4lA4-zFdZ!xԌ(bIz'C:3u$s.뻖Kw3qLD0wCJçE{rpOoSP0a&I7$E&8pK`|"vX(S>u ЖG4'{0B!צ ,=3-z1 k4V-J>I6k5 Q RN xKrnkTZ;gmu2qM:)C W"Y~#:]K$J Vs@(Ew:vD"zAI<2 oCۆCaw_K@o:L0HV#-橷XO]h Wz3+ɰW1^1cmHԭ4Ej̞cEpp0[J"kJ4A yh݀O†JL4ablݭIC%~*Vj%L@2F+9 d.>')])ͼu6 Wi㖗 J,#F~fbP Yb ԩy7}w(MT  EsĪ2 t,vJ@s! \՞2ӱW@L!}4!%ja_^ћ qB5VvVV2ܵ敒عu 5an_(U]YA`|>v'{p{ɯ%_ G ňQcwm#k ARcF]A2^LHcl qJEOhר-ǥ%̶̺ji(P!VQR~WHTOWYeb@oRCp=&&e%XOFK{oVv=\7&+0R -ɺ:`59gt|Sۇ{z&E8$*bH`9$FDxL"j1dTWVsĪ̧1.!{K}-q^7PL` 鸩 :CFB4`5Ϗemle+KI򜣍?WcHP8!ZYԄ[TDc\} &n9}ڢ11QzbWCHR5K9A wEN/ ^7-8 A-⛬vlmNA&(s x`޽]r|/:!꒶:JJnFGLݹj B[gf1cX:{fߊP4@%MWm]l@Imk#APqA/qe;,>v %%A|h vwΖf*-skk5*or`|x^iIu 8> Mal%Qhaz2'm Dio51{ mnmoA{X1.l6 C2ƫ(A iyN&U/JUaPJv!uVzS+43E]W|u KN;ym8Ǎ>1 |>w(Y ] #8шIV_bIgj"b\oS/ytJ%> 1rάf_>9y9~GR_ԍQ1 33;h7@T& c\t`gvBhP "C"2@EfΠ^k@xKuw}CY̌\/%J뀍sۺ:X;W~S :]Jyo'k3PIQXmq?pxvN gZf+=g -m$Gr3yg3s+t_Kxz7 c wC@   푲\Ky!HEXܢgE"4&Zi':w+{~`bs3WfK"m~徢/˷l[X3F[v 4QCAFi4ٿ Ӊfq)k0cΖWFv܏p0u쑕0dh=ԟoqYb#R-sU^Fv7^Fl layaA/C"B\caGB .-yi2ȹBA#)Fƌ+Q<%]\5]Sڵ ޔFhj@b un%Ͻ~+c}PtAtHq^SH V)H0Ջb,/!0&[fQU CT x,|Ƹ<㣋ɸ6 xlt,K MȢ|}2Xu䚑,15`'PXյ \R qP6㓑vҴXCsM/TGZw!'=f{\a")g,mEކ3chP<û uPT 6q`\ȄU=(Rl5O$l ~sсϫл/ ^j?kp{{}EM+4SP/ѰM z z L7ӣlY)Xʷ_۔]WN dN,4!ZOuh1&t;Ȕ>b2_6|Z{Okr6b=1sITLX%FE.HT\?!3}+7>-T SwN\N5^8fUKGj;*sIN6NOo1U)1lo ""{뚑3ߊ?PCZ;zyv⋌X uO뼳"g$1f('t$gEwo;Cx5ۺ_.ਵKٻU|z>RC۸oRSߡ}BMCkry>3sU9R8O}tNu}7O/g^?Jwz6wKdzxNN5IcĶwh3iw:Yc(yl^D~ޝ^ہkr:BS Ї;]ʨ%;88J]h)"Gs~D$4H[u!޶e& ߲B7载|ݢ}}~)絚䥔t;Oi%Uyv*4KFD4UբۆU{}TH.s'ҿ*eEb$[-c[$$-^Oa[!7/qX]]VLԌ$r؋DߺO-I,0wuM>w|8hTh#%ols!F| [|Xʚ>h03ZdҽIew׼$ѣihIlޠn|=C"dp R#KԉndNF%D2v9f,'&yz4Wt.8m(ӫ?;ޤϪP׵s7k2h4'[nٌeKi-W4֎@@9ҘMWGWk*0iP!K*$fU]W<ɟ.ش8/z J1"<+ vaH@q6OCt=A6EA)]d+9Yj {`!8qcϑ8 đ(tXjn@MfCܢC kO DkVě RG5CF; 4Hj\:M 9x&~|JO3ȷnA.MKdee5dlm NTyR휺5q"8sP@IVXӓễfFIut!BaoΧҊZu[uJnw2_+4`1^myE #K AnO$gtp:B[fFlƽ o2ca [PYc`=74ǵIeԘ (J--d=9Ӝa^gyQ?:KXjZ1X:@%ILcPFiYO3 BI+"w3CbUh0zf¬f V[E)E ň5Z'Bc*qaCjW]*@N94<2 qf EJ:#dR4/Q@M2jiLm'"x n }+}* U颱g)Mޔ) D ޭ~˻uYSE)Hd2Y1gLp7M?nؙZTa&f+;ARF&ifEE(ڎL9j3.o""t}:h %\iuĆ7,O[CũqgE$֜hEP\S4&nskh01pg>i^Yghx5|8XR;0b =duUK y?qIPy~.0Vaɜ#HɝuƽA+Ky[dA%W-3:_Xô6"ܭB>WnSnϒv-Cԑ~v=(E0[ չ?F K6Di$fDK'*AOQkrk$]d31aJ#}Z08n'W4!Ӕږ$4 [$pkQEly~^t vZ4%+9&Ԩʻȹm#803Mr6YGu= L~>-% nIPwOة#QjHAml3^cƑ5S[qen)~o:[1k 3=;xg¬% |]q(yD>goJo0~O뵱 7,bH$[5y(wW&_Cz-E(*1?׫eG!@́>>qK..% y5ӟ?%}WK=2vKv++ǨSCf^fϳ3Ƚ\qZw%r~tRuzGrk#:mLZ?7g0ɓMCS#b˨ՓoP,R:˵P>xPn`ˎ{o!yd؋T p&eW=n=%BLw Wrݢ34ǖSS$^KYgdRܟWQӚbEQR! bt=ɝrpy;IJ Q\*fNTꮂ7XҴtN4?Q2No=Ejj+Z%ԙsgOyc kvo\a3G˹["'%xԌYpԝñҘ3wN࠸ϛAuGcz~gmEֶ& +5M p%][ `'YJNk߅D]ְdy>0{+>P D|k'!b_cCպ 쵯7֡VT~%aꓮu`>̗o[݃j <}{UWcTpo:Uv.9PSV%tx_OVf&H9zVp[ʻH|q98D~RK-g Z_0W\wQl:k VmQ:u*ԪǠ SGyq rXocabL9R-۹PHƟ&// I"5E~׈Ba]: .~ʛyQefg۹u EU!Ƀ/Ώ'Ks{C;;}qBۃR;sٯw^T/M& [r_s|mb -Gh\=-w/?5r_XBsU5'rqF_nНTk^\jNUdDs8Jc'ݖ[;g#dF4H^r9/ӊTʨʻ9Vf$*Yˋw -X񎺘 'Xy46?VRYb_Tapv4 YJm+Od_>|7_ؐ0R%wDy+i&GtH0ҔW\ϿEIKdw⥢)'*||ginqbJ^) 20"P|fg} g3̯.ǐJd* K7ӂ+VyF0W@<-F=?/3bNig pu!:%@ X'^/R\z i} iO@#O}uhXD}_6ouۨV-7XOywڐev6r"šFTʞfc?E?s?j烉HQ;SHR HDzYO ա>5W \dSnR6ĺZ/ #j߲ۤڹ*=r'ND=qw݂Ϭ`<3@=doNZ">5-&/Hjq.DS +kvM+[bc-1tǒ[b憎3mraK^n-Ism#VŮA7:^k.Kut]Q_-Vۂs`@&s mD(s7l6^۷dЫEeo#FۘW+]8j.`s,A T^˃l)x\敠rhYEM-a6s! V$T0pF P}FB/Ѓ).Îkȫ^*Fڗ^Y1K1uOo"Z=GkpubDH.LLvp3&z0;Ϊ8qu68S ,]OZY[gMnp8}1К7[UZ'DDݷw~s})g^c2 ޏ0sAϯoϖSyS]6o#ѵI%VA>5"̪jAt5+ck`OG,c/9ZHjo`+\rz'>)$WMS;Hi(]MV4v2}#6U ^gV5Ԁ0 43&S_'cDsWW(T9D\9c^w|5fB5I,$e PM*-QLmVpŵAlL!"8 9;)M񢢧]لH 23⊠d4ڗ<4uh:\ޤjkl[20͢I]9x!{!@m7+Zb沘ʓM?NpB|2feRV.T#{ZphuXAKjxš6bҡ~`˳-=}O@+vkSΜATXl@j2zW#& IJ5XI{6p"gUBPՓ*H]64д.%ڰ '$e%3@04*}2]ʉiO8ʀCc!!Lf@<M(jY6Hgj"_TMJ#lABC`U)̠*V)*[@ү ri-jePZu8J"C2ϜB{Xrnm'`N}H\Jby 3V-HrM$U<7 PDYaTmuE6fQIG}`CUqQLr;\_$05!d PUA'易.>^-; ZzL(+ ncHpyA+MJ0#߰Ul.⡸OZR꘤K N7~lhqBPLz9P2`6ɖC2Ud`'$s J*up1-SAL[BsfJ^=N1;mz WBf%d^|-9r Ʒm,V~ m 2UMxQw,Ӥ"@wNeG:]YWv5OAKXʼX9'&l!Ž{fFKcH?n\37B%6Dv/b) b7GTGY䢠z֒3#+\#ёN71un6G!N l_Souz!'|T| e4 :fYrWhI\jkB((@Uy":K]g,,3D\5n}"Z3J~H^z}Km~QH|HOWgM9ݛgHx*OXy>B$S$5P̣ ^~R@9lSEUhl<Y2 *mfuG_vB .|:/|G dz.,rcƳwr'W~ɾ2sWJlPLD;|?>Z{U~y{N4#w&7[4@ m^޾l9wl}okJOMɊd1E<'in8ec`Vots eB>Bf#f`3>#CUjuusxI} GfNMŤ!ِ4+tjG(*w)~FЭP>{*x`CQ0e"^<_<:{ Ȯm%\L4D`m@32ИcnRm.7^WS(k/&-HZץE6s GiFK0|馷qZaZ{vNbpV?[!3qp%5#xwK幏KҖȞ'lRث1w,[ H̉8L&F hTQ\E*VG@Z g06-J oyɌ~ ZՌİ$Z.Jl0o~d0 C6}Q&&vl m) 3HMD`w:ČBiXу0յHL%N/_*Fק8-_Р2ZyMC"RSC嘩m+Sԋ!+ofwQ!БWrZ"z:>Vq:FZu~:k,lB'na[}٫+Tqw4lt ÷?H1jUs 5P@cQIZHTPLm7hjW>r#LsFKř#+`$<݉YQ]byϬ|uUO|H")"FN(85`E3ɝ?_G.XAh^H/^=O k rfT0O qIox8ErJG.fOZ:n#ց|&k#csC,ЙŸ<,k^n-Fz^=aљPsFAmdYsw^ 7ܧHs!$YkN*"XE'uGZɦsLY&7/0rY|^ ~ˌi1M4s)b R*˷>߈X1̶ȄWDy@Ey5R6v[Be.e/ɾ[d{x#x_ r%? ]{DeA%yç }ZNͧMnw{\\lF:2i I@]JqA+&RYVA?' C&}]5/ Ӭv3lk)l$yk~H' !e)+Rc#\~}H<2F'~Yy@I$M,qc=<¥>7}u'm%)`B[ʾ\mW&'VP4ʦ`y]D* P hVPCqh,]FMEڹdPzXLV<+Qx-QzR3D|2E_—L–vRXBz)幊jK:^O**~zLg-=yfcJϯ$-WKk͑=`3u4+ ]-[7wU#L]F¬п   T P̈́BWNӆCX*ΤoPAy!tSn[FkL\.ӶH)&?u(zRN/czQ7.Ymׯ=gLٶV[?)H3u֖#X92KgQ*jSӸnmjԓLNeh*}.?ěi.ϠOw:^r-\M޸P--iĦRF㺇Yr05 ~6eN!LKa}J<)R#-UI=% 6N )G̖,U _ 0 Sfɰ˩6 &:UVy|D *dDze)3zc-b{>c U( jB*ܤLڊWR1LQVԅa657'%W8|T?.gxӔMVרI˴ 9D+"f1݅:@m=&֒b/7RBN{E$"7i%1[/sV2@,cYrqSxecqO1d"JiVj߷7ƉM;u[ )4C&x'7,_YQC7Mh9 l 6 "bbyooI):|.m}6^M+Vbѣt[KL[I6x+$1ُehZŝՃw`-18OWf JZ.ajn_xT;$ڹq`bZ-w H,3Ԅ^,MKmu` I+vo>a1\ 7M 0?Uy7>vey]kj]eo{7(܃CE{TNp^=-g|=e\ O_WaSKC[2=ìD0xqqT$֎EzQv<~NW:†sCs!{9u6&k.8c5>׸3yp/\ Q(zm 񠵦+q\b<Є;D{د3'#'is' RvH`;A4% eH)g%$ԍpvT2 {o41Fu IpfC+2J喹}}MyUXZ%bcuKl7cSY)coס9R9Ej*v"[KB ݂xX}y5$Gy{-+̳1Z24^j:T,"j*>\JMI?QjF@M`C6vB&z9965- EX1nJ=L=F 0T#SFV|0XOz.e9g 5E OmSpN Tz'C?n0qCta3;SZZ1\.=xi{Y];/(/s E80E!qm/31XeWpbAjЊ.RYD`0dEA(zPP? ^aV Ʊϛ=ѣ(ʈ2jxa|J+}Mq@1c$dp7f؅<}B2A[K(e,瑏ŋ=lԷ =-eN.mTʊ'+ّ<@=AQ[.]}Dtwʼnf:z47g%MaRYV{cO}{JpfM$f!^"ý;oFڙeq][0sѪ9)4H9zI{Cl/f]3d7SA͸( h4ui6j>WNItm/j?RwLce#OYWdZUD0|}m!G:ЭqYpRI9C`_.*.jm>&WK::,a~=ޝ?kt|[?7'y\䟷qz}}?~n{jbN~OF:nIC??_܀-ZZ.V6Dez4Cˇʾb,k=[~~, ՆR`Qe'ɒ>̹+|D }G^b̵5q{{r0hQMenMx~(N pBBojS:F v^:B)˖9ɟ=RLHݙ;eDN˫m''"x゚˗{shPݹ4h4eU= ڤc'kE ,Bb1~Er,%G%of2[jʯ$6P9sJ8hy1$[:FK*iN3W\V,W>SLެ"{\jG%L6]WNp| f<| 7ך.oȞ\)'<ȥ[xu>s[thk5@%]rD[cm!џ풹F,!5YX{AjՍ;0\e.App讏lФ7cIxJDHv 'zvQ}`}Ѧ{s Q@Ҙ\~N!&4 &kBgP JQ2s2_]cP[oc |GA}wsΫDĩ&K,`DkRL iUEue)UPaʡVUiJNZ=~kWTc (_)}}*+ʑ>4 <0%BA=sӍ\VbU$YJ Ԩ[JdK)I;9 Y VwÆi[gDxya@4KF Z'[bnr:~ b"ɡG eN#?{P=&aEb&xHgD3;Uk\m T=HӁez_ B&D$0`^M'}hR ՆNzG--V{a^" P\[VĩOr% Diib՛]9MKf[Lzl_=)2j-A54݁PV|-~5P_K__yoJG[k[1 Vn{-bHq)),F>x r 7Ս&Y +bĢPm04X. M:UQh%M=a#(@ = p^(19J^X[@6z1Ɓ` M 8@7EaQ(F"wQBbr2B2Gkdj&6a- )] Ehr\{VddPGNBvn%Ԡ@U7jmh8C)&Hk^$}Ŝ-v7“}fSo=8?_ANmB `#EïZK۸ ˙-wk7(6?Gk# v„##˲)E@"̩ٓ!W>JJ|SmPX#%Z-9zpt ɴ$*&ڔbkb+2\aY;;^"q7BUo06)cUVAC$PVRwU 0Y"5+e-AoPa;&et^jbG1/ʍfnM)[寯NVP?sՈ9CtPA9~&%t1R !Wd#[5]]"lCP ? t"G +Lu*h`c\Mz]{I'. *{5jWL"Xyh^"87lio-)/q/VuxA 4r/cseQn'?e/|DivSL5,)] ++5?W7o|b8]Bu&blLKhW+I`WJQR"{V uBLS"[7R$m ~#WZl5IHe 8*dJVRgA xV B *PWг&yN²)3C_E4+8֪n>X㫭щgʜ֤"ˬWh%')kKL&:B:D$1OO [9Ʊ[h/ޕ2ؤůJ)F7߶E}_ez)f% N"I6 hA,ڨ|^Àla#o (id/T/mĔ)#+29P]FX<=:)岶Eݲ%X̌'r*œ1n3s4F4|\{YyXj2f@NE؀мjX(H7'|&ϟICP-8ajeB3w6{Gs;r8h4b-5K[%D N")4[BJXͺ8ȇ ;7 AS9e ʩ54!pICu~8X!B\qa̧iȃ~ PRlژS4%o[ptєfa (@qv2d e?WjeK\/o. q`K %i;cGr++OP^]AGmu_|Y>?4`2"/x%P ) Qjp{% |+7{a#Mx7)YK abX&{_55=`g}>R4<5W1 Q£Vge;=U7*1ƨL1k!ӳѴujPؽ=-Lע9O=#}DK,^Ԗw“G.-RCӃBXDQy(*$͏^ jmr?.igI0Xq#$WژJ%utRqQ5٩1! 73+Ǝr{}?/zʖ^r%\[.1>sx{>w{8HN8roX lve 5jTVE:==]5r'q?)4@^;֣Ӛ"#gʱ.q+  kk ]9djDBxѯ[nv~#|$l9R·W;hͱrqNʔ쑛k̵E9"O^k#0Dυ8~5"2lQ"+SSʉ I[7Q [4 $DH\k6x™w7szFeޫ{HR5"M +݀@SDS C<IVG0mQU}`8/Yۘqߨki2?MfM%OF`k2z$8lj-+:r\]ӥR\ r",!{ookd;.j| Xm&v ̢$؁d n9rP["DGwnٱ!V4| KORE_2o :G+X:ޮ},u5@Ғ71k! |zAX`PH& Za~Vǝ(cEJ@; ڭ=yY [G%-]9;H)1XEP<}rFO"B<]4U%raZ6ԓul^_1uICjnD.>E ե4`=dQh%17tcc ۜګ!BIn27$Urxzso@TEq3=lY|=7&}\%Gw’yԫޖs|!&LTxĀ&!1u2t58R+>-fa KZm+d/!5m()\\ϣ|?ήTMrbnvIƆhʁTS|L㸓U-iM ;.=f*.K"㿣BU /|`t0a3\W_v}o BíDkS>5I\|Һd&Hdn1jPtofJw I3{H X99b` TT,ԛ۰qP҄fm.cKr♭  +\k9]amdmXm՗`5bdq8<&;f:f[[ʿU)K*OZmuWyAt[9IILsnv%̈́@Q7e YEntオ&ˍiiR\x e 8"E QNs轈rsڤ[ǭ[\})lf01::{ѻ@f!M哞ɁB }F#~Pzpx{(3wpոRš:s;t1k@<_W  aA,&Ŭ@Ngxp%QwNnon:f5ҸEnDHy1*'<,K>:U5`E<au.W=bfWMx8`bENL`,m'8[tv3aU=wՉH"B p1HUb""-J!1yus Yv4%2s Di Q[@]zx. thzX]h|KВPJ딌/K o$6 ͤC"u])QvxGMGZ. ԑÒLǽ g'ٰs.t߆󴒀|~Up6 yU9w70MG^`JI1HL̈N惠S]A.΄,y,+Ey<\$3͖5ucIyN!q^sD;>(f*!CO|(Z(Z(,e1X;{. " ߬ Pwuq]79Åk&IAH b˜)˳Ù8T, ODmE957velGJQ'C8IC6Ec\KtР~ޏe0l3eS&; Y C'TH?gE윂}!p~% VߞʮWF#_vך@ySCv7+4#EUQctgxFbhC! &|^iJQ$%_Ty_|>_ND~_N~ }],dr?g.tUO=WL> Y|߫:rξHN\Oެx}?z5v).Ma%>;h_VYÌ )+OY^y(hzv%uCcf\{eo_2{l{Hiv}3 ə|]R ax4NpD]GΚ=E^be'50V7F*3G%ES:)Lptzd1nޡMg;`@E,K5mwig=~盨^nj,:=ze 5rY Y}Psn BL /&=<0HAc Qީ,>ee4ӯV q_-^Bpk|S]Pb,n/k!pQHC߻(3e-~Ina>g]ݕ`ݼ먧^9oɎhοU /<5 ?@3& p{Q[=Fy ;aP#f"でWh&)O"SCZ~IYYxt=xG5~n̂yd&hH>ؐu o>AMml3*h`ՐyVT#K]Y:ay,q4W[&T#)?0{{|g:SŤ#?K0][m*A]$fWD8 5r{voSW[cֆ F@m5HJ_CbkԾ4 &ch ¼X­F}0 1l LZ9Y8 _"d%ucA#aB@Jnh+!f1LDQD6Jh2/5=pޡͼXPT`J:Ďp Q@Y49bAj\ U^FJ A1A+S /|$O۰q^Y\ ~'<ʙ>fNP4X6K%(hz{hvVMk\)z(BQC. 'YN/"lwnAS~$aqؤYب^j j@lf ޘb#mLԸ܇I+RT>7(paFQ$b'@XiUU9 -y;i i&H":jvTӘ a3 WrNaoƉQ9tQE)~!п"M ꟢l춌oDMh#wLs'ᅖ&;7k5cgDvRa\H5`.C~pa1(V93k}- K nb}2( ot?ORn@;jP4x55Edd<3b2礰|68};9܁@iN pX.f? #H5+m,z+?TRhiwc:ycZEqLX 7Qr1[\~<|M*=V*t'-d%P:Zh ~7j 4r}c'*MW0ӎV ,ú;u#1pAs|4>7 tOHDoev`~dM܂2WY[|Koef9^43 1}:hPR)aBRiyu{=N(z!qIN!+zgbu*Խ\$9kA+zaI[XC%4-r[[CJh;'Àoh٦ .)#xڲ?#r7%d(dcW0e MN`Z,4{ ʍg=K(@ D[1[R(cb .9x`8?41+"GZa=_R WÎM0J@bgRLcuvXI"[VxG2D{L:ی(dZJgߊ$VJvE$,Z`ǽ3(ԠÒˠ)hLY&NgzJ7|H.]L {F (.=\ԒT"㴈33XXz!CΉsTv(`[T,5ݓu; '҈& G^ f%ޘ@ֽ ݵmN3x2!G݂ץcj͵:͎5+ͮP ms_:s(cfl\_hӵ-W/3pw. 9?^M,ݛ%£6`*=j-:0%C/\pt1r׎eAez"DȫЎeD@8g0Br46;m7{ Ҫ=6+E5-p^K64(Ru NJ׻Dn]N[Ma6P.>!4S8_!c1LtK#M ޕLC=͓hs}yH9fT}P(nn-.B7;_vүMY29TPxv/k r_.m7@_ڠgeNP`f_H!^ xcGcc$'B? F;\`Xu{?/]=q1i}R>4q~܏a WǮV}~I[ r6wX!'+37Z$($&gG }֖=eR"\ٶ/g40BE҄F UɉW Euq`fup=)F:sO]9 L/zz$ـmz[ަoS?nJdY$<n? pH3#aU114p^ !_E:'3ͅ;>ۉ>BK4xtStFWP()ߜϻ7Qe3.h u4ޭ) Dqn6WQƙid,ǵZBnJ]iQ4@Cj y$kv:WǛ1*=z;0m-?h!H܈ˏ²;ͩrLb`ʬ$z* t2ȵ֨ʡ2=2-jA%7@U31AZ,O - 9YOK)3N| T{_}y' EoB9`Aݫ½Ϧ>T \`N-

KXlP%+JA=Da r0 8,b wL,F%:馭2!IٙTl|dL"# mH)$<(2!\uU`zQKY*|Hfc(giMG4VT|'F/ÀGHO|j1݉V2㝀8 Qy [X|>PE dDc!;NDLJB)RVD6‘Fm*k3 `i*%^aWܵul2אX^ aKfrX gp?>j'Ňo?XF [  H;YjeO单O7.l\m*u11}U¢6s̈́0c*@IHh~x)pV. #|!%VѪb[wɨ6v Tve$a .,μӨJr o mB\YC[]:#@GuE q"qT=e&& ӨTy$Yx zq59ãq+Q.CFǜi߮=R3Ruupsߒ=m'gJ#fV#^Ž衠QNI\4Qg-e7fHĹ#W P9]Tt]уwmj/Ӵ :;v@}7AՑ**uw1iT쵐a #҆<-iAgO&̸*>׆Ofe;X,&"P xt4j4So :JzaPo'H X,X, }vf--Pq5@fQd|'޳{8BVWY XZ' pVj3@(ρZ6]1OS۴Vo5* /βa`TL$,&Mue-㍸H ƸI >g&'.Dזut63%]ў"}L;;ѥ=/.V<[8\BbF7_b6FW8[G>|z[k&z`R҉{f7Q%6W*-+&yHj_?7uzOm?^_f"(6_]im\sjK-ߟilWI3e<gSnT[+oZF+zUĢbNK1o_kr~QQ(,~ ~mkE9t~ZSQLf+ne,TuytvDzy47;Lƛ{Ŕ{-2{|{>~'i}qus8|Zzvv}ɺuכ.nvrC) geoHwʗ"Ķ/O1yO~gr{n}=*m& @[_!p1+3`+S*ŕf~&[,]ޒ@%$F+>RgT@VYc`gw?;!,rAŒ~Ƃ#AsLXt֋K9YC r]n{9%x=]ɝc@Nӟ#Ðڍrj!Z5g%4jh釷YWpxea[H,$f@Gѷ}tRIp> llSWPGIErm҂ 0*ni(M' Byl#hjƷ B=ӯ’>#- }YVɝ=r8  Eou>R [ex>{;`!>0fP2 P|Is3%<6"m@Y:̹W -6zb1f5q@\y{GezA0ʆXlQ=VuawM/[&C62Zb. M i"0o~ѶGģI/in𖨩XESD0\ >SY!SMBL,B[XK:Uxs'S8H]pO IWWUf7>sUd/xH<҈y٨)yXtޞ) d׊PyKL}jB3uo6I@" 4[F`x! .qSmFY䏲;MǿXpkN=Cx{ äS"&_4m/~kwJQ}}P1t6A[tn"ُ`o4԰MqҞdw AuL"]|LZ#Nl-!%ZTs*`IᵣN= `{}M5^8(3iA]@ܝNm [VHlz;t ]';K;59xkwkAwU ֐%sg3>܁xwqphgm{yj{ןд_ tuRY7_pg`gɝuWHїOЫ2AU1>,܃}b:q6k/E$ >򲮲"b&-fj-@W-q@ܕy*gޗ3^jNNoL57]D,~٣O3[Sr"׺&aCRAX*Gu_9g#`r"֚wzn^;W/_FS@?' .ͼџ.Б5<9=)(>W>a ~+*FMXg3!7]K~nMsd*hФ;չ#ymYH֪dgAͩ1R9"]7.oI:(nI2x?b|8;(5n~vGwwx88 Ե#3O^U{#zS3$lqrD΢ qyݏGt <@uU9}Q/uklۅ@|MxxiSHը/mќU/pkwlNcU 8} 8oU(#G΂r$zup@QoJ N7{@e Hf B v~&#Z֕kU9A˻*vupɾ{u3E߉L eI# )$}Ʀ FSe9<}#kAH.$Op7!V6YeU4s+$},8]*Ād =6SW4.:9h 𔗍PAq Jaw0-еJK-iWOׂ'R.UVI: vОSZ~?Fbzbyg9 ̴\8Pon^G.FbAFaq72}kM@>v^f^?!oO>[UˋS\"?^n=3`-[!!AG4s|Lt W'ݍ;z-|a(ZǂV, !嚮b w./P&-|^/~G]3)}<5q5BIOFc G0Cۈ u>Z!1G%ŵy-Nsr88$jn#vi1{%m's:Y8Wt@@ұL{U/qln KO%(2±1"_v*gCx )&1U!$ׇE ܓD|%"r-aJzʒ?$_O^s2r*ƻ17$df" Lf~):xoKeo|?),B%?CJBOݑycЅώ݊HVg&DT*s,)GF wWgLr__1-Kd?ظ6k1M +F" H BA^cD Ću] irל< sYk'pEl-.V5bp>Yk{B:/bQYvV⦋LJp0#bg-YM.?c bp|$++x"]⑯(ռdg;! kr,(( WZ7ƔU9U3'}]wDm`$Tnc0&H̼vCN9h4&vLup]"oD rKݚP}6wgS:NsъPęɃS;sLH9EIH^"dsKP;l,ϟ>+p&FLKH/?#1D#9TDmxT{@xWufwݰq6r똗W_u)8XEnIΞ E$ȟzP5e R0/ѩoz{OJ|X[ ǑKq-L7_ubʿǥNv A~ g8>@Dk6)AԌ_ BhǾL~RKwk5r2 ڙ ; !˿ED3;@STq(&](P~Y6[ޯ]lz~σ?w+`j-\Aifa[eM L~o۫?,+ân 5{g]۪( R$ߠńKq 7Jury0w f\"YYFx=*7ȶ{.t-l+1)@\J n2. %0=չ4,y#ܮwtʵR<`(%DO`jFu"g#6$\C'ӽOד)VB?tJי3EǍ30XabĶ|[cJݰbeBk8ztznWzԜN{tח[Y`਌'EsEDIaɷ ;%N!/JWW7w[g֫h; ۯܘT#հ N)G ]͞m-3vM/VóEQ/BAܤ*2Sx"JqbՓ&?t; ?^Z11➨Mhwo=]ڞ߻jjFxPiƋ5`6x<\|2C%w\<&K5?hsD~M1`Ņ`%E+(Uv0iⴵ3Mlhɪ hj7+nk2"Gȣ*M >GUe{#9\u${20k9T]I ؚ~h'Ku R엓asoDŲ8="UdzDg mFbDfbmc&Fn-,~0Rj& }XB`|reRn$5Yz%?u9ڳ'f^֣XE`xl c=2ĆL|Xm ߒ Y`(56ZnI3_jg>'pz@DV7kݫz#UIOTOI5-}2jž(_@Dx (?tc*emqT șo"0X\(Xs# ?WH^V>cjfQ5#B_|۵61^* \DD@+Q]rUZՒ*!"PdULJk0}[/d-XªjGG j$"T6UzzfoOiL .- 9)vNUp!@֍D珄؁ ISQ|,,Yۋ(kH a~>NaXC(XY{X@>~5犷<73vYM{ Y\x Οw2 3%'[ՒM$ :q'R<!^ZWN̘.f.r LJðe{w(#\*e|!U2O$el h0Ʋ'_oF4+ϱl<e /&@y-*DEy)6_SSPmeZ7C ,<'D&CLֶnd>>yck4/m bINFRq`JRgۮXōsn 4T+թ{1 XtjhiQ+Pv:7)32 30H2ͷI:gI|-X /M^W+F/MY;4ccxwjB$Đszr撠 H~9O\G?@ ⴪;B!ґ|1W1? kR:Fɇ tTGҳ]^X/GǛ6w[Ocl&6[VDx6"A\Xܞ Hk:U\*j[s RcIV^ kR 4fz3` ]_n?ׇ92ξ$$zz3D<5 ?{p9>;3H۳ߓG!Ws%dbp̈0e LV-l1+ӆ)yW;߯6繼?7ΞϮ-칺ծ;cl#GHM†&KډbPW`@0~)D*Dx,H*I;-eEIVnk;0v5) |A0E%P&wtYqnʂt' hbZZBK`hx;kRNH@|vDQ)"RD457XKX.ͭ./ Z-ڃgj:}צJS|:fuXBEIAfb M{d&r[EX:vh%ug .y w$/#$v.d[RY347z rGЕfkalܲGǁA3WqJ@6q,,i0R0rd$gܥa$蝭ǘA!1d#M5DHԼC uBHd,y'Fؠ|G51ܮLpk@) 61jD%hsVᵄ)Tn󎿮ʛ.`Ma&;ƒMx4#b4VϼE1˥h0EՓJ K]9GYMTKHo/5 s*nGP5[-tu۬Gi-6vLIi5*u4وܙC4r^Jnq̅4_edF3 sdDŌ{bDLnjTV!Ȳxnz?l≺'ĩ g }@|d2KP3LDs™JԤe%u\,-җ3c< (ޟ D^b{/mDՓ@l-fuTl^AA)^ָeraV:`cTB,ʨPW ZbE]5"Y,,6e{OV112U9'*CuX8i!BIOgm fd/AvA؟2AkdL=XZwVsf,>-&wOi>>nuMT񑹐z5cmZЦc͑4ε/KJRK8N߉?Cgx:!d<0THvVE^G$s345)2kyIi: RDgt?nC: C0ߧ$c_]:>}>yϫJ:6dsSVc*;c`i_4I՚=fy+j]LC3FaL6iB9*M rdˋ%Fl6kU),~.~:P@ꂽ 2:E69NQ6z}x>PZљ{)f!zg) i\{AJwzLM;j.ݘi/JvgYZc*g Q+=ΌX rneȷsM8ܳie z,6[f/X-,,srʳqժnaxHdT?1 e 0A5:)7&rrpE5d+^*ٵ.*eB‹,C`vmk Tm6'A4&킂By8BiGM;,sr9F2= nV_J[~cZMC¡dw6"x s}%VZ r,s>(M[isހPcA}%& %\uqX45UӂgYA [߅`Ф&PQ5Cm&[-59$oD,j4&sF'S矎N92TQ/,-͓#J.ٻOK6U?):s݆b!cĖ~^0ŏNʿH'[۝=V=EҤm'Ъ0␶\-E. 60p ;x dXK=fPmdfѯÒ_֐叩喜7ӎөъ&*7.#44^[unDӚ2WBqN]DϹ]ڎP9* ٶx(i IovFlw8s 5\^o>r~dr,R<<6DL$E6-o,oOT3{z۾e; Y"R┱z QC]wV}rlAI< q 7\[:#X?z񍑉5ThBR=UBl҇BFFߔ'jhb_*&hvO ~M>svWvu׽}ӥAyfrbl(xP$kS#iFµ(Z&J^nGPfk )8}ve?7AM$X9SPdC:--wwD`h*N4[D| g)B ;o-_G.ē)0< 1#ȱ6#Xņ&o9֘ q-/Vߗϗ]?NVCR!cag hi|]"1t͔O[5|erI&F]fBq01{쓐HRџbDo Rbo$r,hp{lXD5Vai dr ηF4Xa9A|Xa1AJȼ 0loK^lv *  `gnWϢfiڌח&55+CN>*[@0 E"DzRׄ8oaUZD졅G-.E)]/Z ʄz0|Nƭ}8.IZj+F?횺BҐ|pQuI\L&l3a7bv$bt^ wFpy%V1bEl Aޯ=O2Fc$7n+AOSd^-b1vHs(-KqN%r eNş!Is̴YC bCDhg-G&Z>Go7:"]5"JXKb2ѲZ7qPF^#Ja\F~)ES60nAGJR@q"/UW =G*GnuR"UpIre8fg̩h| R(^5{ˊ ;wEL1*zjW,)ޓf*+ѳhHKxH!Zz|TCͬ2gcgѮw4TD%exWq |r?3j ((n" eNGZ X~R,6(kPjjhyvexI9Dkzyf$/ >kԧ  O8;ּQgdeF/谨u|XJL +NDŽ5_"˖7"|C/"O2-7ֵ%pYm-1 TZtuD7ph\2,OmfJ>(h1 iR\2aҰ}`G2feS01n[4UG{0 _+|GGd%qȓ]^,jPM]wJWU,Mc 9]gFi wL& !'Rm!]WPLJeyQy,-y&Qe*koҥeY ap\#)"1uIw"*wX.<=;q[9fiJ;QI6jHcYťxbEoь{FLӌ" @-iہA*~ /A =dRT:8;m8WM=Mt ֤V2hƘ'B&L-~ 5m~O[l;nRV0Hk ʡugtڸUK/6eNM՚4AI,XnȝAY>HM( r])I\;Ce]1GNPեщ ~0 0>iryxr19j.ߎ1GqFg>(>V#'t\e6ϱ xI.bv!4_0Ћ҂Q4"I[AU[e3%żoSfM'BҍM>$J'N(fl\v92Cdc!grՃ#O@6f їU撔^K]%._rUOd4ۛOk@Ni >Kg_I}i[029{+jwU%[]WqYȔvN9* Ih㟒G>۾$Գ-?KG #©גywܱF_K;j_ebS+-R" Zm͇lfˍ]Vs "[왇^ 8"Je>YVDA@V\cɶ#ⶀ^9^zlx|Old]R@eS"¢6:uPW@isr1db8$ V[EM:RP!5n\4OO]" äw{\*skۋZC4r?F.6|J \CHDiRK>>&Qz +mwثErEӸݱ-A=(䯠aA4SLW*[n9SrH1lۃ\6Ej~._j]{|pL,<48@[z'%>c"5 lb}ɪ۸L>xnqkyݺAW"S;-sg?۶Ub}2H0䬈ք 67G=t[g ZG1V6 ~S^w_GBhTc8'PEY}h*Wl_Rz_^fߚ,S):@DM9W96ڢjhaRҭۖEѴm۶m۶m۶mvi۶yZ}-/Χ=#Z#xJ]\mwlX]p)y_jÌ!l.rO& eƪDjn1 48=Ij[=?X3uS[M?7E9?@- Wmޝs/iWSi9ݙS MLY|?}Sٯe)}٫w~oiy;r?SLJXW8|Ӄk:rm'oI'_W]G6uŵ}H~z1݆yD0q2)/]v)*GM=!^7lwU:?ͩ͞nw n^sTK2=% c%Z.#Bx%< %̥%J ch');nѺL(/!dgІfN$J2~.V߯)ܜ5(7ZDFa*$t`X 5&jؔxdž'`237E%^$&xhOh؞qDZhFR:šP3?4%)4zIMu&AVEf wK܏w,Cƀ\>CfBj ~{tl0AdWT:|5T"4< )tgrh@i%!dR02 b ^YpXh'TrN"{}Vǃi?>??{wVŦSY<r?'{f~::+уPnY6.! ;72+!5eeDHbv"PdJ Wb#^ש(q/ݫg,yWc[^AX:"aA=(}UOv|Z؝o??E0#enYWՄ:"r7h_lɛJGoG DzՍU $!>Y-[zGX2({v-p:h#$?"fPШ($΀$5sdt:LoOĘSsQkeh"]P. \QeT+8Gcѕ䀌I-:igb'{"t ak~U8XĵZSS783nlo @!R;IGN>~魇j$Ŵ]MĞH#D3i nӢd%) jE-lfu?YX;ڂY`m r+B_hx&&Pt<3B5ѢN}#̄$s8C-1˒KZ,T%%eLRDJtH@=jLYZF qr: ==(v7|Eo+d1w{`V#Jo:;2+P(lQ:S&9d8~!ifs#:js}W,QQx1-x3S:-]/S(c> K*x,$o6IMh"M=w i[PQlIʅI74Տס 5il5%4K()BWppP9 ʳܨī,W,"-}{Vm8;Yf {vߕE(^+:bb1gRl$S #MBicP&jR塌*.Qb%3>RڢHE }uC0<͚&KױObm^v=cӎ͙%ZVCKÄuU ҥ*$Il{#vʢT6Y:'2L0g>k}(z[OEL]2PJGJpOի1ZuO(Ѱ4o҄?tD&4'_lc!ELõzVqIW$V\ l[ "oNK0lD;iŗd}{ma@uO1$?D fZ\m;4_V_bەz[ڈGI:QLZ<%rۘ朢6UG6sopb"V$^Jb!c!9o(B'\shۨ2ۣ醍3)}6wO~mT<5oRv|Wd7EoGPxPhtIht+/*`!l**>n|M;%h)X;ev~[gijV&xyS &;eʛ"u|+s)vP譻| +do>vue}WUqT,{mGo..b6ԺY lx­ʵil\W<ޮ1${A*tZo 1AT: R)%^;;^j7-mGZcR?UnN$ԕNpO)UtQ"L첚U*xy,L*p۾X[uSszɈ,O5gEc|Ho}iR֭%~aivNN9 i:-xՅ~ji+iI1~nA`U)]E&VX'USTEv'ڄ‹򶧇^sә@~pYzNh ~4ڂ.mn>s֏UY'hWʷLLd@ǭσg)4V>8$ly˙eYeJo\(ᱳ(Dwq73>ZWdX,YMmdyvoGkd?LV.~j23 n6LUYP?TGlͻ]7{wgxhrv&ؒ+JޤaoyI[Tecd:?(P&.B=~S S6 9$aL^~FĶm(gFI̺AbBh\|6zx^Ϫ8z K+ppYWCC`n*h@^m!M3zq^ͩ0zop#|>w[&OVM=E)b- _g@ׂR5DknS?SR.2E0 a6H[-<m􆯚͒-͜Jۛi̦͒:S9h2$"ePDD}Y&{2])tSJl>`76Թ5y%]G$jZ9FUeیvޒZQ0 t*a }_--k%7IIT P# ^:\7xlx Z;{sOQ*Fw}f&Gn7"u} d*<=?W)~n+mΌ g)7AeG!$V$1LzH&ىqys+SCSKmbyAj"W ^:^yF K߱g*uŢZA;l_' #\炖:.d_p\`TN_kBf!OJ<%3,3)_NgSnԴ)+nmBhʄݙ XMfb"aU4Zh V7Pm,ou-$P;jwB:sڞ-7@fEĦr3LDR3\.ّk֘Z4kqXxBvN7Y)`j|rM>LO69%(O=.{^>HZj t4F#iRީS=Q<|vC$#LgI.&ЛHgW<4D&sdꢎDyMї-pj~:C;C؁lr@HguL8eO _]\kzS6 zm r8oy0G;{۶Z鞡:!$'{q.wyDzؗ0v9z1Oաbfe9զ[6Qz5#ʸAj&{IJ1/>rpނc+8귖c(x;O 1gb`6 ?l0WZ$.f"N )+lvA+7BDh L%fvﺀ4PUki4k&t:d27B~ >7?ެ/Л1W~}?pNIyq;|:cѦ}(qxiKmv&eFifSog;|4nSffpVY:<ߞ$[,0wCF>v2z!/\磻gw3T=>5};>xu1:^Ń7xU~rT ߓ0A\#k^ښ":&^}n/_쮷۱,Bxp4H &4v5% |w0t)]'K{-%M90hPJ dCYy4c5jFjV~T.\ϣE=;*Sf h8 }j~uNjhꚨ!Y,!gr\p:qcO 9Bԯ\W- Es%%,`3'&D$&tx=[FPdgcd`!vfKMc&%QLg-dnZ-p AElp5t'*DVOu3*w{-]9zR'^6h&K'ѐ O+֩`#=AY!Ri\jI3쒊fBz7xwOW:E 4< zCHk\ F3x8|%mxV8mc2"(ٵxr1ӞBT8!yrGLO^dm9%K$3AYrRAzhgiDGD{Y& #xiW$7y(6p!fB!1B}Gx?<@v6lgvs2?!xjJJ]O8TBrCz1V6F E+W2FՅuKiab V5';<Ɇӆ E`*Ɔ[!xQSJ,oQeP &y}MzU9ú-hS~~n U6i&nh2dBR:hb}7ٝF_)cCu\smMtm2rηg ꪁ_XK]e_8 /rL=>U\.ZV$wo_?>s@ڋ]w{rߝZV}w;Jvo5/ lvS.Jij][Qn0ּùIIzv雒=^IJf ¢sIr7~ygwroF _ۈ(ϛ PJ;$de, m rpm;m83o;^˗~aqnaPSBm^l((AŎU0rOsǢ=]o44\\oTxի<ţiOfpү6ZnɈW*(Xd`RnT%\ij>gՀ?ZP P(ڿ8Lb ϊ"NLǜ@fp*))v*AxE`㕗۞-,vڨpQlGxx>N7:!ׁs?Ù_sp;?= -} n~b80s-u% Ao*(s 3TTI9|cqD H%P>9#ҀZ/zʼn!)b S!{չR*je1(ʂ5]1 D6 6Ρ k!% 0A6#xv;{!޿bO=(h1D3;c5ԏWg2,ӭ:+@knlZau{xIv :a_-9.5%H/a#_I=[!zf&sή;u1;f}FIЪM֣PYH8h& e'5yIP) `d-j=A5D WՋ-'-h\#JWnZֺO4ojm&EkWYpc@b7*d̶SWs.OYLP0'ڼZJ*JF8H7 pg /cTjnٱy9TA Tk!{-\:59Њ= V%zT1كzA;ADr09`b/Pf-kxyi%9+R9c(?.(ʗ'Az"5%6hZPR 2mP!\~E['T_oTlb\.Sf̚47-٦~eA;`eXKW[!Xp諑`2x bvz y mnTgKwCwIn@vgC*"To;Xò!:T+f&e}E;ff̅zmG؍nE3Ӑ$J=R[9im7|>;8Ee|:d#G&_0a~Xapoj/]dQR):Vx>#asaNBl"ŷHD5z*ͳm̤l*=sXpqc#n*DI*l0GӠ6?X!XIPtj tFfFr<H"g )PP_nov*F CpH FSpv s;UpJ-Kg)H}Ybui^+ |Uk:3*|~htg&Cyi%t+V"E;k46owo n鱙fhz`_FKASNᨳ\Z& @9= 'tR7[ڑ5MG'o6 &\8²#.C.:tKƛ{2ɈAtbLѨƴRMJѮ c-O‰g20Ku;vvmh)}튜V2V'T' o7v(zΚ$#WISAy۱A+7cxQ\n&gBj isX5LW"X1`5=L<)9RaN&j2 b&/f4Q3NI%W)sa1[f.jTH~;hZDf}+oo.BtcF\-c>HΨ;[B'gu5B<ܥRT ̕~:g`jS9 ifqcImUu?8uYQr3g,P¼̕M|~hv)L3ysW +ynhu.Q.E12λ]v!zPri5m\k 񇅘̝܎7xAbiŹ5$cȰLK!Cydc z@;Mh!!Rlo n l J3aA^ -`? nJU4i_2-^TmU$^~f؁]Ys9Z:Ow_eUu6֡6UVQǧz#l5tudp\<6RZ;>[u :8!C~DJ F)C[[kQg9 H?wn" 9rҔD|o)wzmy8JQ%A"Qhωb4J\*Xqo㉠4 {bgs 4u-׊OeN]wʱ楾qBЈ3/@M6[ Sд<ݽNVPPPNjt]wmOZObH؟#|"R G>#"&ko8i;J= zʁMԒr`䋣+]C;rY0@sV!? /@5ma JB /g 'ö`,. cD斲@UISUbfKa~$#0qnά䈃CG7]He}^_^\*S8d19_h{b."R*d~rUV,V mެiT:Ux`jPߺS㼣6W [H()^d&̋,y X AX]J+ŇIr(&+el } P`DG#~I;khxjZ-ۣR+alq%@_,HZZ\32A&EgDJf8҉:ptJcNf[=7b:J!oV 3mі??)vJl*`d,.6NڦrZ=5=boDR& b xTZ6}ܽ,жu`;FEWp`kpToi`0Ģ PdDVq}=w@BD^Q!_ , rDP9 0p`> fGC--(\&a U4ocEPVv}Á =Pʅ>;u I/z-pc2F%ǖBd LNް,sV+'r9( )[dP٩VPĆ~*M*W. i4\?=Z}.@h9DϦkB[iR'N0m;wɾR43` ȡ&?3I{WڻUk7oVL7HMX#ڗwV6=V4> )rX<Lj~7_njݚm(~sfٍʼn.볥4vg>{[L}Ӑas[ԮtcMbXnFZpVh<%H!L;8{pk=5ںd}JEFɪBò=.y2$q6Wn/w;jyK`]'$iu-rA/o9啘p8p] +_aw}*eLm L:;cY*5_=I--d!u B(À$<="`I.lVa(U@G{b+tkf=rv/kG (Fj^ #~H&q#\hJ ;\#/ R-‹` @.ܿ#}ѿCj%SbxsZ-x2sZLpw(iA8$B{1@ эh~Ͱ[J_(BIJ֟~;F/*e]{΢M<\fzyxd1gK2NJba:Dwgl>kBh7P (t0,1}l!{lG\2lDa{@:DAMZw'".9НUnuH!EzAhM$Dt6[{-YyHἚtn%ne60o#j 4kM̘_$vuP"+s|$+ȧ\=F"۩Kj-gj*$J J񤟪qbd#Ue c"ьxu5W=-kZêCg=H:3ns X7U]~{se]H jjbTi08duw6i|Tͷ戛Ugrxf*uTZal_N!?A4$-f *n;M::ee &o>^WiE!Dm)ԩK*&։ /6E)'wۓ5hMgWL[8k :v[P1yUVg4Ohot9~կxmv{8T4 7gHUt(gt^_:4M=^YFR tiG4Z؂z?!!V샪̈́ )SFw3Ԋ'՝Lwhǩ\vtb*p՛Wybd@1T#ʒEx5>PY7s !B=q$ep4y'o!Λ^8i!QC'0eA_pX,a݅I AJ|-'N^ƒYy|J[)ÐOknJ;^MHQK*ql .uZ*=,;v 7NK}[-pAQIM?1}Sqj+z#sWcfezw] Xe)+]̕<NCB=<<. }|iG5:OaQ!̒PŘ#R(L?򂢀oXJe?EH aoHHvQ⿻C犪PdMpGG2C3fa?RiMo{dZ_^o rϺ>}:|ڸ@ݦ~91]ro*1 }V R̬͓ gfZ^!q[*h W~ qY3;]J {ě~u$P "cė>0q!_fC͝ਪ瀞IF+"snov` ~FSnͼx(uΎcrEt.);^}< 'qdyJGs4$XPycUa$5+݈׾u]nIbxI c2d_RPifP4Blbh*T(tQ=39I'S/h1A^riE> Q,IHDXT?5)FPH$Df$hSYMͶ~l[DVki݉kx~A@IeaF\qObxkiƯEbrAVI|ve_ĘwvJU-TOf!Q|Rt$aϪH! FB% $D GoZ|E$: o] - GY3jyz {@Fő,VzOdL57A/:fv(>x,M}/u6iMSJ{T&ڳNq]0#ǐɼ %ltw2>ml&iZF;@W:lj (l8ݴ74V\Xb6f@,j]32 pjcl9oBp%Tќ8E0>ߢ(=>zKUmq*O3r\_C|L?F@+/Ut4 ?Vf[Cp`Gv cdq+ԀΊƩόnM:Z6E;0  BW>,́n$2 <@Mg2gMZtQ6ͻ2b|jIrJFv7l=`(;^ilcWKgGs1.6X\y_fdZ`η`d{#FjvƬ3 dP6BvTGoӄw426@+3fhA &r,wͦ7 K9m<}K5 1@cXΘm^¦nS=/1Hgiu3XDw'Nyta~b^ TQ<3&bV}ŒVˎzw6hǥuS9.H=tf"mj`9nqmoM:UddO'{_J A?\wٙW0S<ФˇQz6ᅢeuܭtE,ð  T+\xe,R5f dsn1ɫhyDfdHn~0 7>0DIQUU^ځb%;5M/b]Z -$TeSP4TZWu(pp: gUퟌ "b{,wF[6(o,Po(&tTDnHħHQ=^jڦR"8`\%mEFַ ><|Ha2{~t JpN_Qq'\\w{u)u.V-abbPJvRagoTR4;vV0z 'C?"<0d1H󒼿V*ѭ+RNyso?m/ao38ˏYf} Ha\aSyaN.6ѽ!_3y|?` _c+G:Np޸5y|7ế|9p ItK>sS9{ty|o_Me~Qřj7^#rn7^+hآ :1t<5  w*p%4-L%(kpLbE>~iS)zrO+k>p G iZpnLC{ BLjML{8"47'Rd _`Bܲh%v|b'&\7*`uHڧgh|#nM⠀L$wK0أ5]Gcmi&ö\/ 95QF)`fdΟ셳57p6f!4,MUU`: ty|kYHFE^NRh%\kEc8%Ǵqg]fӬ /b̥ ύ3M7[NSr5#I') ITEz>q994E0 ;Uz7:$. k[xp! s{ۂ+Iq"SΙƜɄ\vL8Dp) e\ۛ=4T W#Kҳ) YcG^E[;}! x fhl$ Cs$!)v'(:(gO֘<'ՁJeZ'J$ZV)xڼo7}*D[,&&ָFE P 8ٚFNl!d:4Uc2 e!e 24pDKi'b$Bztÿ"Խ;%W~ܢtpߗ3,u8>3KT$@F2V>5άaO1M0iA3\o7#9»8XJ,q`\{1%UjgXGz=*"0wfju?a<7r)^Tm@%,d"+Nhz]N&CUnj喎MS7R֬)eK@L9,*DhV|P ;f5 Y*4ڥU-g5']vӌhbR *>zY]E1p/(7eʳI?cj 2aNHP5I98y3,񾟹^TSqj hfǚ%e6JDgP~L}ξ[!m?Ehtps9ǫecn1U2zD9y* Ƒtwq}bXz<n2cw!j\3@ ywG˸` B,[eF!y/oo)Jb UlƙaYWp'߳l*, Vrq)ԫxrGߚ+dyFZiV8{q[byNsT.ɵ=QmT>YW207NW4YgJ䞺 ji=2Gߙ®"v~$J0)"h5Ĵ t|L/:}-u.46 a([BGTk_L")1c54*o·鐊ٛg$WBϑ/kS!Ё?+P{r3,olyΑm&ln?D[ELE#Xn:?鴵}2,MJsRNKX{"Fw{|TPij(^|0O~s=>8z~{c/ߙ:zΕFXiU^z>n'c]_y{W܎'fo̐?֕9=qI՜:CY]SN 1C4Cd87ĄQ2,?εInRyAi?^g~x;rKV0p贕%K!~%_ߏJ ҶZN/$ _ԧ\Ly@byi9X!Kݼk}mkKvj\ۜPZGˠO+>a˽x\>68KӯumG޾QWإ fqݗ03uHu`oR]K$'؄vhp.32DU5&T3}06ov۸=?WQۓ0·Wg?f?)3~Wg4Ud[{cŋw2Y_bVmky0kQ@})KZCG1w0vv|^Ao>&q΍=vG{]u\}ݾowN\x/o!D*DXy^mpűrjόJloW^ƳȻLvRZoe<)wAbju^/gGCdgXC4D.Y qο|1/jq>d\ڷ@Ede[츓˙oa;2LVLp+NJ+ ƴߥ1Bw'`'̟~7-/C9Z!e3M*8A%,^Dv,]^z^Є r*/Yp`(/gp)X&Jn?<&F.djQ{J+-Yvi(KrHN w9)SD\6Eì9JsBUi5ZP}N j!_Uf1q`fk5h1^y-ȫUR A.+p3T pr7Fe2@;-}ǝfd4MxxPY3I'_,\ QN2iSy e(h${0Kj#Fث-`2UD0;:H/KWMG%4b42)_gd6.j8z!oǔw{:(/ZoZX3BX} ZVZ'Iݑwy(ty x>Z\ UZtH (#d Cy<+&-Âc1 O SbV7flNzYP_6AY Z/32qEd'/Bçyۙ7cmJ+X!BX/Z%靮OYU4!J<=C/ 0dC% 5Ǻ0`NQSi."hTFXmJQxOLe?2_RA~Q^F}kǶ1ˤwF!M_j"z"_f'2He\bE&[oD;\bm(%>q'gl ކGaUB!<+_3y V 6T8C)A0-tM9 W;(ῴ9h,6>s9lX#iyxj @@'ݗSdg3<&<|M@tU+`c`.M1<3ɉeFVh!Qd@4HHZEaŷIeiruI顼j873N$FTx_s5c¬rfmGO6 wהV9!$/7IZi4E L&`Jm]AGO`>̑.XDh6vm Co] 0LKSd[^Ydsȓwd' ~޿BREykʚi "/ׇ_\%Ģ$6+p0ɡJZ?};{H=¢juYƊg4O$̺\XQ٠{\Mu[QRYLk[Q:ם}sRJE|8P- Yח"yt PkCe9õPsx0 ,3()AiK乤DJj)q|O`ӃBUŋι{Ry0}5粓le `XM *Es;ȹg@T7`f r> >&Qq7j)L(ٿ'a{[ Uc]QɁM5$+`6Hۋ\ar4Ϊ:c&?v%iZ9ǭ /<tFSa/XJd$"/E=9_ZMy.$PBtLP#!Tp\[u,b ҅f8BC<8doDt>qn'rۭ bbgSJ-vȮX)vW1SG2ƾ6Uj:DJ5b2 G eO愓x"HRjyI4m~.{q+j-yi^kUquH1U#gyXLsօV ΍Ən1I03fB Xu bpZEV)I+k mZt&y;Ym.~ ?}hkqwT/WF8=ncTBxdSӠjO!A)c:7<{?slK&w?-'5}Ȇ?ox)ؔhs;ċTd5k.t$1ٸ'[2ҟ N/u"IRh=\P:>Mu*+Y`u֤x<*v(^K/pkqᕮRRxpں0*e Lbi*խ]$+2@F8]4𯃖p^y|R;9ԩ @b7(%Uj]^f^X/ Cizgj?xg-ǘcDl @AbV0ofkEiʈ}Ln7DdeE{+:PUXuIݞ"e@q ΀a!RD(QpG8hMŝtqiWy_;SiqӹQRlcRh1q qcPx0P;??bY~rB*ٽxz0g/*i(5fb}gjtʖ3Q)Ac2`FUתR=8YD/IZgX/os).ڊ^2,?|mXo@l5Êqop.)5h8i51cG E/ɼ8J{2Gcr  g&;sr,Wɡ_3zn?L)xqzI @Eu'L5>(9?ĪT&xiR8~_vy544gJ [q 8.eCCS(-Ɛ9U+l.NTCӗ[e:=ņ4ѯ;%؊c"s'S A]O z8ރKF IͱYf",\OQX[4h-7dAqa3L"%^[)PaY^֭Ym"T`4<\Мx\3d]Ti}[(3$$3ήF$) \rAOb-iG8 1tngYRr!}G0XF^@}RǽjPQfZNR]O6a X!&x@čҕ:W[.յn}asyjC^h c̓n?ר"pG2FӲYerO0QO4t#\l]zt QP;0pB ߖ }fy 9*Jr\ݟePjS<@f#U4 ?l:efDiql`HenaMe)'N}YN6x<hwغԭ7!j'a%5O#:xH3W@[z H[An;"僸"_/_8v;}=eX/"7 {:98[Owywҟs0|| ]v~'o-N(@B}3ay_ϭKhx_OwvsiߡVRl,--f:QX j $IoO8mff6ճ|6 >gk9 Pt"G >aS`6R {^AU07ST7+7OcN\4"\iJwstL7{ XԈ='+}!;%Zoo+}oh/Uv0wv61I!BpIaKυА;E-pgfr?so UDvM`8D"9?ל":B)a;eJnWDa- (<ֱ4clG Ms΂1wp# 0GR٨]נFmhE 7C>sr_٘?YLfQfIy*u SF{It-+f >i} @ԥfT$ er VOub.$Vgּ ]<%X'e}g,19 UOW4+&سȝB8v Й03r6\IU3 L[˲8]R:7 8T[IR6zx2 ?iiy eDӟ lD1F3jKVs#Ԗ5^Ǻ&ېPS7%tdPAFtJ-jWm^R||Y ,g4n`M:jB^= LSQN^@ տ\7Z3v8u5lxO:fţvS \OY.tܙSQKC0>7ߤBđvU):iURr ScH(*tckp@N_S2'X$%a0$\'Z.LYp9hfe8%$co Ɂ4gF"9F'a_R5M(oS9R4B:Rv9\"bAZtkf2Su=5ˌ=ZjJNN4[aB}2 v\gՐdiQM~mIJ} 5 q*٥xw_м_Rίpv TVؽ/za7 RxPkmP;S4!S7;e Wj& ͞H壅l]MDA6+oDխr{z(I:gk{lj"9~ɨ) P]JjhojwH3i1)mS"h!TDL$ڄDB22ŧacht5f2vf|f,2]?i9&\ېP=̲⏒0em{nu6jVSJv;ҽ\?_`T臭b^^Iq㬝,ҁ[iDD,Q \6Tݢ9M )3:-T+p6phwCr}fZ;ٔ4+ƥ 97lvՎc;jevu:e%g샛ԪBnNwI`i5NOó~N(MkCFz+iý+mlҭcLKw <k".Z!1+Q"g~d/p?DaJW30G99RSe!|d OOJ0Uݮ-n/Ĝ|VJ n/yXug^9Dy|k+{7v/=]p a9w1Ny#멤ZPڥ+Ivܢ-ZMsk nҲP'3l0NW۱D6uސ6&K6JIom?MHoPY:y4.}n]օ*ݸ[WU 9*mr{~Bz\NtxߴZ+@›d_$ژf~B"Sze\@|-YӋ-`p~P:tG rXPɱH:B%H#0`L- zvѶ!M5&`$^tpl Tj=aCn7qD3DmEYCBsrh{G,vDUB/C!9)?dHBLtQxQ;q"e,\à "4K%'9BДsڰ|2(ϊ1z G OUψ,I{j 8hn9h t|?(5딡)'Sdc^63d6yY(FDXIҬx.̺28Di 1`G#TYֹ loy>ޞ_u..pHd)“Hp IqLAgPqr&$PFBD[ EUh{K-\T4v.eXW8:lwGYjtl̛n'G MHձ,s] Cq[A ^ k7)>A8`:R'S1xH]ΐ(Iס42"IAJ>w>s3@5Iyo65h]P,LIMuUP7h@i+0錉?ۄ(3k$i+gۊ2>A GrP<ʣf OsM #+穇_ iȐ$5eJ8mg|[he|{=H#J[E$E {LP6b즹dʽq;s))l6ܔi9EM 8h2[0&˃6*BJfH^%n= %~Jo=¥W=AV9n%m^gTJaBS?6sB.@֐z2GJR)yяfW<3 =)Yȧm 01űBxp{bR7 `FO%bc$uY#U?G? L*7ڹў5G~P! !qskHTQ_ ԌUэ0z}>?*{_?uxu/rpxW}}w(tLתo׽J%@0# 7 fgtv 7'p#ϻonDzN|>|x5$ BX#JHd;8 yZ`gYU6HNpQ]Qn qr%<4D<[PdrrR?'cN:X]@˞u5H=+M@Q$# U ܧVgg"}jݳ"Թ^)KMε h.$42( gIM5/Mxl7AA[sD0a,74^I^.*4&Ru چv~>B9_淘Ib~Bd2P1/a"Лwo/]hD 7YⱽJ\JO$xt!2>1xBv*VٵC%׿4E-msUM5rtsVC}fy۩[ecV-s'+ډ6'aؐu"RQJ]v3}(m'f^cN<#Y򰚡cD r3ώR)hxx>eCiF y=hei~ڐDgZK[A}J$'HG),DĶP?BiA{n+\2+W2/lld+ͱ Yӧp tt%CLs7"/ϬbD vd"Piœ(LZ3!HOUͥlIΗ{MB uԂHFȒƦk>u +i\!ƅq!0̌*]ׄY0R//xAyqkSaGWpkH&{@b`gg 3ܶtvIRyIqR1K5Q#52Ĝ: Cl*R jAfzL 05 7Mzx| @ܔ %_HLw^FjP4~Aw&3# -uٗWptK< q/ɡ?>8Y ݴ;JupI-£˒4DC{Ȥ2sKEpDy%3?ǾkM}Mܼ#4L`h,QgL_H)MU}% X03_W'ZJndhS3?xk'ݫËk>vpcX3W ӋYxaU71۝cmMz({oUMi6Şͭ]~t]}~lb6,bge݃bӰlXC6*״lAtdN_=nB|3:Suy 7U>{p(˜<>z oox(/߾ۏO\kp?Sg;7ʿë aMhw*5A9aޥЖ7i88oQ8 -~~6㥿k=|wOVrEyk\;˴`m82},>\G2^ӋSn.anMŗ{r<%א8$ xn'/$:=wA*wE:ihOtc ť0OEX bÃg=@rAY @RMu$~+>=zJuU쩧xBW1#_ bGD"EF7BiEٷeRF<'d qr\74Vznj+acSn/k݄qܧ.]^ⴾ_1 |v}tښH/;Zl>HO1v֖qvƻXʡCxʢZYv\N-mMVbe*-ڔI#:qfWj$k}+%zBc^b)\ctKlY[)$lk>lP~mFYS)1S7cHk WtG,(J ìL4R vqPx dJ]Rq4O>&|}}@2Y>#U(f yoy0`T3masvԦR56ҧ@C%dʢKjMAu۴ꗳ=Đ kE,6دn75䧛,i.. Jl#:NEm_ ~xu<{%P7+{5S^eUmͭs^M5[:(g2BvɼklTĿy-L&ߓ:D۲medgLP16aHE3gd=rE & ~ITyDfz=Bm(>ӫSt欬\g) ;6PU5һ ilt Gpoÿ132;01ߕ|]uv*C951Dֽ˖QF}jaU,l|8+? qtgz"=<6g䴞ξsn#[v,=X_/?wjm̯;j]=* ~z'˝Nqý2l}4Լ|,T-TԼ\fE72M젼M%,,ٷmb?o*gifL' $WtI' lFZpoR9|o^q5R+771xۨ[ٯ 6FKo$E_nsoM0Oz"O:20F dْ^ &0KZ@"gphe>,ʮ`Db0.nf%-.O1.àhL/mվد~VvP|{IvfP\xȢC75.mpENtk zvZLy5Fѕ>֛< ޏbd<~!Jl,2τ?=FH-J60qCj 0ve oE%F` _,&+!ȵH:#Z".Awy_8dX_-Eok¨|hY#}< ( k?c=)LZ0`'_V xu%yCnV L7=ULo^R_cDfqr!K&6CW2Emȁ B|B;B cw#e_!+.k|O]}OKӦzϨ!rz/d*Y%(^u~QD"4.-5-TcrEL_cz"g2RבDTwF*LR'|vIwo#iL {=o G ~0]~tm>RBkp0n- p)< `$7{4iz]i:E#bj{҂D͖'/{ST԰E$b/@KI{Ml/r>Ⅻ|:!i~U+Ljqejn\@QKYvrF+F_F *áX<-mMi"/yQ*HJ?dGodEGƍ&D`essu5_cсR5ֿ: ړڅ3fr/rՋrq $ 9,l/e `Z& KV 0)Cm7Ԗ-ӻ-b  Jjb_9_SV~(M4vOL%q4JԨ$'[ Uy{0 MW)u@bxfԕ!ёqň CgxL{_B* zFhZel4\bN5-n lZDbv뉈Kq9s W3܆ YvLkzxc^VP{6|{/Cj.:p7!-D3&yfUɴ%8lmhDĂ'9CdSuWH/`(To/X:ۦkCabaV)<XzBNʂד6r1(t>Jknsɢp?;`L,Li23/&Yk&r\!}u LX :;R,ϙ%%rXo]j]Kiu{9i8yN֙}MܝO}#ƛΩe<:\-xlNm)ߠ0<\MY<7(f7v^M P+_M=/z ?(%Em8'7&Pa,,{E{vGPܰd&1nS4o8pC+yU]:'qz`ol`R heFfɀЦT2Y3waسI]Ĝ|۫tǬ.#mΔ?:94 ML_ XkqSDTy,$3H~װ©dx,w=0B!BvˤE&n 7ѐDSBkEpÝ;Rvm6CAn{!AOauk%|tM"gJROwO@DgUG0 "R";sHԩ6MY]x`tmm# J$+v Q>xź!>{Ѽڿ~x+}S۵!&%6Ƈx*J@> [! KKM@Ӈ'%|<ɏĨRک^  he>= IVl7h `2t`VDg+1%B, #sOQfimk~нmɘ5|8ՔOEA 0Ib SL;@*Kj«/laMP0$FO6qn Ei/سm׺ʫF2`ǡu.&7QDhTo&0O6 CȄ*%3]bxX0SlgsRPRT4PO􍑣  ռMv4vMS70pUYK|o[?P!.2֧qZ{?#Gs~F s YwaSAoد>qv[XMK[s`bF~X27n\{ >C[Sq;uM,4_W5gʱY~uO.aAu ͔h^M&`V Ǯi[l|<}֝YZ ` h}0U/^3G'[[lJOQG,;Q{.u[;}{TH*c_V:“Xb\a Xs'0٭&n~'tLC)q'ް:E^&"M1)YKfqq*gviv(GpGM,lxZ:c(h 31TB% x(+e8zJwp"E{Ic Yf8Iyr0:e :|nWkckw.S|RӈZ<<8'Φ?~j>4}]r#HNJnZHaõIwT?CPGifQy<ޠ֦mƱ&24~ o;~S`pOytNDZ/N^?vP/d#&EyWq"zyvy_ 1$06uXѯ<@Nj;Ngct 'M0g;6 I- 5/Pw1 ;o_͉53c W1*tm8BYaYɟ\z䴳A[+::ff4_4s,`Q$7A4'47G;,dS_5\~4" f{;>|xOղe׈\KC S=5|G~i Ѱ:0w:‡ihJ3LWso]VLkvc M+{!&+a]w.% Oydc-#? ٪cn 5h &tj`@GzՕh2r%B If2@gnQ8"KK(\pMA7Z'GoT_'4P껪]ٞv"rHk re=!O5/%T5%GK1(ʿ7yO{$HG ۯP F#`nV}aIiuXG) VS*޺jq0; ;2OzOLNANĬ6JoM54s88P(A/(QAU褺HgEB`fm~y*" *`z5:\ph 243EKPi o:O)*K\ lhI D1F%3r.1nA7SeZhQ r[|(6(dfkdq ҄9s'YtS֣ ;#>e18fS$ʻR3>Ѡ0:KFx 7M'ڣ2ӯ׬zM&e+lKB+uES>;U|Xa[}y.rdP:"t -=)ϲ!V^tuH;2IGIg sLjmNoIC"]uP<}VO2#hW] Z*xÐJzkU;v%:m&, aJfS_]~VY,vΖ{iS ]46{@M9͋"Rɥ{tlwQMQlp}Ko-^lɤ"o]ȊIRMOSh8I&M??}Zd1(4Lؐ9d*g9PN5MVqr7֎, 9g<:+`QB1 osbzH?0PFM}C=|xlB.F|j\+L:;B*v3Q.+W5VdQC1]ڧ hj^'K+=teޣ]wS/?Nt90ݖ~,4 { h."b^xRo5'/ KRɞnZ>1|vIoNB)~IT( ki Qvbe35p>.[ s@nCH+8[RA'UQt?#)faGJ.Qp_"rVjX#bQbL'&"IL`FsJ:$qmר8M/K_8y!xl%܆ )ٹ@x(dU\(zc6k2?q>hj%_r{ko g{A/=WG^8׼*O2-ϲ/cխNsbxr8k bob%OuYNrt|vЅ;GX ^ t߷}Ǩ,dJX)GKG.%-[GrM<%6=n {$rP c5''h~5| zI*Z@2;:&˔jCp^Ȏr!9w67aTAw\^Qua Y</p^YM,0RҁI:՘sqw,-7[Mksپ)Ar܅6Y\Sײ&Uͥ*SˆHEmCh%A/cMG|w^_e\ 03~wBƁ2RFKO俋-> _y#2rcEtAt7@NjYrkcrD/Λ0IS<9.e r.Ur1 QuI#L;~񱕇 z?(ݟ+΋B<Up|8ܧO;ɝs[?B&TrF7~]TDz#G( jTqZqYOf`HׁN-:,€ ]ى풙M}b63-Oҹ[$D<:u v&aJ7jP>G")ס1cdR`UItiqC3^(#RO#Ͱ̺!6Zjoqwݾ8\{71 j;FxYt"R|;i וeCK]QD f&H'M' P!8 B1 Ni&ˉK2q-iK0=LpU`T`pumx<N ^%(^ŽvTKi*(;{7RH)xp- =EI\mR8, Bȝpxa|gN{|s]XYAk5Yvb?s~x◗Vx?tbMZT\^!^JkhER  C>vˡUO;s ɯb's .׷*͹oYzзv;(!ڤg7ZO Z(i9O6hb]l]d\X5jGv$-6ؼ`5OD[c -FC W^El#a()6]s'EM (ܲԂwQ/43 sMDRE! J|rrnIds6ެ?89- dF($ \lyѻ5D&ͧp99)Z"I僜wR@T91ÅxI gǥؿJf@~/Jx?aY("r[/sC\_W16h_ڨCrUT;ہhGm RVɓ.tIΟ _Y$iD%7E.& gSe؅Y)[H}2ɾ'ǀؓ~:Qd(i:D&%y ^Xs z&E0B5uuT鸹`GyDs9oF_GÞ9E,jh0TVM2++(=Sxj e`$Ǭ*І-Ө)Ԇ{7|IyZ{fa/L0 Zr~(Kf:v$v z \PNw)?k#!.,%/ӧ/4]BHj_M6.L M $jj`f E`QX4nR/"?8zUP)hՀ%#WfL'{c&v$XmVJtGn]3>e?eiB гa(/qk0/݉ ^Ӊ_Cy<3 ,QJ&}9>XXb$̀ _]lOd0D8榲{Srj_ ԁmS<{:bw-{JaKn5:Ԝd2뻤5xvw M-'_wry)裎Q2MNę8 ( '&Jg#}i(8HksZ^pzRhu6_OH_-f&p}iU_oj k׼7*f_. G{s7jЅ2 d ]m>;Nz,` ˰]!`kgHgaLQ!~ ,5Ә\jb"y @5ty7,hl,V1 j+c~S yu#Fg6a+7P#{{o_~6C..݁39yu{;7Kurk.+ ޔYY)?nau WM H6?e_sygz3mJ߾sT7v&dz uw .|Pb)R6hy<;}у1m`n$чeUX0cD*/߿ oHm a*uT=e{$hy8Von+Ibbj&C+S>,9.1T@>qۆΰ^Be_{7ɍG_cKp9{/|])n+S~w1ų9- $V}8Z`ծ˰<zY^*]c OcigK@ԓM_0eUsI0M%no,B_PJvU^}J Nڝ6a‚&A BvWVZIUurҶff:IOV䍓Z0p.mL*ׂQ 8hA-S P* 5uNj(b州TK[CƆx;2 bE%2 ׊QDD֊%OQ ~M;PDWH(II HA /L_|7廫?\%]1_VYN {ㆳgHk)8KK!p Q-\̵s Y5@wKEOߙ:ߴ7AfSg 돶 Q/*4œ~['qw_pl76uR3.ՐS96rHmEy#src Jr~-,)\>"y2S9')5g"Y,F) ) |LWnu(hlv-`!m lB;M/JY)qg~Jג ߖ@{ SU@4IWvb5t[x@2z=Bz'1}$8/G/gwǷ jpj[q&ܕ`3'5w9?i]dm#?ox7?ňAMNAQI+=#U3Caggba0231ux d`Nꋍ{ ?aK;/zF "F@3#q!:fX̮02 Q~3C=3O? ׏/Q`*{ߖ6%$HH3ȣ|WDy#aEDAO+L0{ V7i29.`\uϧ\|MƎV :OǧGw@|xr2b^'Iלh, s nysuNS˝?j٢x~y; mvZ)nun$Ld/N8f1~]b[˾? hUX )Ȳ5(8pVΞ h]V[=A|cbL3tMxlvgux= i8ҶQvJ}g'FX< uR'ҭqS("j7`l-p7IlpYP"8|t% n[ryCDPSK9LO y]@1C]f 7AZYm'r;#p.2 {'mAER) ffix6ij 5 seRh>g!@[ t`;Vl͉Vsݽ|+= e49auqC! ÖX^ԴOފ6\Oɼ ^u*S` I {D Wd3ӣ s-Ưgش_"?BW1o;>Eh/W7.`Ll$K).PV9&H(k,נ^Ƕe8dcPfgF TLtT;~r\BрTL8w'!| Ѭ|ƨ7*-eôQb4%ȗL .K-7KL{I7㮋ſi%i+P>.跛Y+9HLttǂ?V2Wxc#f܁z/c$H&IHi*=!0VX2dΊl{>YA2XV' a\**PKErrtu{ CsCoxKn$#bG[So1lVaF+@NN "ZN_q4oO8/&9)T16n0͵?bmXӉϨ >cdyG@/UjTgx6mdӡ[Q$2 VKxʧɪY!Y@*WYj˃WLwA?,!z|D-S- 4e+fh֦d  ku* Au? # T"c~_ߵ[( 1 b˒"KLNvuUs;:=QU/3s;K%ϯg RNf[oydT|R1azF֡V^,%ܐ^ryRuF)ZzF7h< [ tV\]r'.ߤ+N^Zi~}11Sx]Wa\{Q4lSHrW/֦aQ"y*5Z v+$xbN8?i;Ztgb,gv:mr<sWSgveW_>!/0%弻d)zyq-4h~NOz!v>S¿?gvX63v% yxBS *NXioIO`VukW* `er ,ղv psڧfْzGmЖH;- iĩl3n{c ?f! .4Q*Hf[WUЩ" {23(mEC\PuꚨL7.MS<^;/홧_C&=G N~Hgq0뛀~?VpJR UvGa!ldDC@b\)4QS$YCJ(9hd6\XVd]a묷ZI^W〃Sϭx  sP>m]:<@=G[ NhQ;@"TD?!u8ev{J'(+YʡӪWǎKVTx% .Rm劀ʘk}n'^¿8m$(2^Kqa92=GSCEUmϺ^"U|_ۇw WvTxQ @;!֢-Izg&' z-@kEiY8wWh]RX sr@[4պfeIP 0< ),e9;ϻz4[S@RaqvQ{Fe9fם,\R*e{P{bh#0ѩAg* z7$6DXUfh?uZ(Vv۩Ca ]Ns;I X, _ı:o|_666F^%U[딷TOYbhs9}8gqO,5.\m1U?p"kKo46eh0CU: cW'O,w]L?}A*# DqWo"?A/Қ?EMm[31d߈~+)bk: h `gU^+%H'ݸマ=?̥ןAQ픿;zM%.T3տP>J:a>zJOKdjhf ٝo~3>EL}.& #n z)wcp]<#|{)OpץT%iB"$ߛũp!@{/YU\/?WJZY=Uhd#jdK'mFKf֕qRʥ1QT5 PڈJJ] R0cK8lFcɓMEu}8;?%<@UBs$zBX=hj}ϕxu]m "̺ 2cs o3& ͯF`%a00otPN|:]8UЄQ,ib'q(RYx(>Ȃ$FxZ&oPs?yGB]KYs}N H0DV9?rBKJEC;% n~|CHAӞ2ϴy 8FR)W`#}gKLX}.¨e=1ԐHv-CqÑnccAҳٜY"J507Ud?-|4$rN^&sqE#3Ad D+}#08AU:o cF9|[yIm-|YKiZV&o$&XɒdFK'l)Xp,ϼ>/Ig{C%ʹ n-74p_4]"޴2T.kFԁ ?  v;R";$ޠe .1"m#0GkCpH >3]S,@Q,B5d #/}lw82~X j^5лBH1,ii6g$ۼW؁ y;H>j b^1>l4p*4Vxzb˴gdWP ?4am:6NqȚ&Iq.ڤ' 71q׹[:Ltc@Z#`2R~b&(!/i;CNtco.!؏A?W>; Hlw kX}:5ٿ¤oxRWT.tV0;3=M|vj]iv,O牗M')d2jjÇ& (fsr"s(k0 uSUh/f*' ޶{,} Qwa^<>Ud,|PjɹТ?|EjNG1 #!ʆK*, -@n0ޛZ2Z= 1}qq/r*"&*,&>]qi 4#*mU<=8ybr lD<@ď5ykGr|u/'ܻ>~e*F>[K-l}Lo,nVrsGM,YVm7mXW4)? (y؉6@Q5lDs=7~_/o)0);wZ=$(ʫChB]֚RO+%,h_2U%&Hs1Xt {-b,~9p% Ob:q́C1[[0,{F@&td2(}kg i2^л=r$IaX/ ._.YXpkGT;0eRg9;U:Z3ܡ\3QV8 5T];|4`h7pmtӥ3ڎS@[5Jە|`. \5ӣ 2\T'L!o8oe"/LZM~7|hK)%'>‚[W|һ&Éa%ًo7Ϳdڈ;|L`M\%]Ю2~7.l[Z~&QWc'"I۪ޱ#QNlK(w @b\K"hz)oH[2644(vdS9ϱu#>|Svh3z^W(hrrOGߔ bߔ }lƔ3.hwR ynJݎl*tS77S+Tih{~%Ua0^4HjF l2D[; HH?'uc?Y@@9G_VWԑS\eТmۥңpfqzIU>>X nq4zԖI{m1⣖#4r-@&-܍RzK.LN̹,YsfX+Ώqmp9?%""] 8['dV!EYSMBC&}a$#:vh:#NSn* Iw[ZoI%@c29EiZEh|J/4 #S6b$ i!2c u qWXl޸FH\klv2Yvx&"AoVs``^h_z|::'}Y1edjmrPJջyz:׾ǖFh/Fi8$#"8&f^!EU[*#/I~;@@rCJ;QX KܺVz~e j>\xm+JaM^ڱ+TL\U>BP^;e 0lEs ÔZzL'GЎsx s,4Vm+$gWqI{Zt&1-n;B[&cDG] 8wm\%#|MgoDzvNYFz?Rǧ̪A5v8j}*7 b]+MyH̤\fKzޅ2Gu&-OF[_F`4O.#g Ɔ3qF׶1v[oOиd%\.|D9xSi rD- (BubyڃV9dj vC4p" i;NN5"Zo{^v/ 1iZb~zusqB+o/d2xEb_ OCx4EHU^8z`e)`mh?C)%MW9rQw>wf357Dz!nTT7;Q;e- E4ÿ3/ɓ618HLYA|aWnКRNO` ] iUfe0kq][-_RzxvX{&0 !$/i^ ˗c]89Z.i4 Yg=TȿėmpRd'Llc";ruLMJ,ϔ XgF#ԭGmͨ7g}`KJ:FA9LOfi7W8b 0uy^"(ZZԎ࿩)Ay(r7Zu Qt&/kVq064vWL?TtߓoJxgoY-kvhhL!~ʳHKi%xM}k.ft<_^{RJGp,G>hVBtc**zj.)xqLNk`lznnխvŠ͖(wP1?j-r $ڸkcC~sZ :LO89mCV5.5炿7 )oy)R!By=͝ts\}Kxxp$޺~WLLZiA78H `̬s׉lA =&s,G+E030it7LA=\O7φl4=M_AA |OTzU:+jR*Ow:Egps`D~RҒ̈́cas.43BM]Y{]G/τ̺Ֆ*nfs'.|uuM'mnFgl*_F 1!S~RRҟ Kc0Hh*\ >l k&SNy3hPˀu52E4QʪhiUH iXkc(U!X AEiWZN=J%(Qds=8yu4OAZ1O3a_bP0JZ Lwٹ3|juǏՓHT(V8 姟8"XdSmqo .IR$w$ -DWCQ4y:{˩׋(}D")ȺsU\cH⟥S666 Y^t2i`bB-jD%wm1/P9F4):'ējRDB80һ ')(cEDTwEzf5DOI:$a4i "P h܀\~ٶNeWLC4(H)z?BB+ Ia}@w@  %V4xs[)!ۡ& ܍8FQSBTĎD졳B.Hd|Ђs9Q{HH(0>IduVXp:,1S4mO^C-7F䘲 4F >j]1T)oQd5?.cU:ѿiC[ H"ĘTEO[|pԩt9QC=J!Vhļ1`%O t:MJߵ=ÏV{oPs$,>ѿn{Zʬ_0jїed vA]KhLhI+lM3} ku}#ܹ%do$b'>8M>Hf>Ff@!f+0 bqw@(K%QOjjpdWqp//_n$=if0מ!rl.ei^(s$FV 5o3:1 Xӑ˨|RG)8cbUZhjoyW*vD,*;]r>𡮩y+jAՆ:Ù3(ө ʭqa(ڲ;x1uD鷿@::/g{`u / ĂOp:PH_N-?͊xX[R"~3y1gW 8p$c&,Re&;(,}Pɦ8)>T6:[g \7m+^e"C߱ ҽ}`؏gZť^r9ٿ*Sr[&%TՉ hZGcD&S\FK<@,L]ZbcȔu@1;Oۏ5zWᦝ[a[=GTh$ř1o]o68;#C^̈́͝2Ȗ)̿_n LUl4Ϋ A v~~kfs'#m^jYd^ao8J4aU+WsiAw+Ok~NG}RF #ÑCJZ=D_ۄAAu/ygX:5@Ykۢ+f{f`'@v`$GYDb1cRQ|(QU]YIPOeuk~) 3Mkw'dRlB|"Q PV˚"_YxX.r?c""TDyU7ՄjhɜUDq@ȩ=M1?"W{MK I(~c 7`ִ =/ѯ yCng'Gdasp!4biH*k= BbRm뻘xq,&Z۹ag>s>$Gu*WK5'P(/2p6uqҎZ1FP,)pFi4t>guӟ q~jO $眹1NLLQT X@(WX#7FS6J=!z>sQ[bmZC+Udj5@bFP/<PTTM(6Gx O|ƨLe 43)F|z&N!APZ/DgP zhv*bB"}%CB)0@2)gϋpWTud"LD %EԶ!As 4 Prѹ毈qedz.(D2gnf`˓lk8M2ӛ5{^TiݘA0$V.چ_$CR& Nл*~VXK'O46.FC|[gC, Sy#I|AeCSa=/4gMN۪*RE*8͎]ʁ!"f`mnn[|u Z.rM 1ՑG38f5GFp]{ \qpp^ qe]S(:_/dT O'ԯ+4؉=|G7ۤj11iUs?+ơ}*tݑ`{T7dk;"R(ieI:Af1$}fV< =k/ 5"JI"+q]@G\ ~oM0ͭj]OW*,fG`wY)9 &Ò{6ec9^t6b1ШV'prR㕀;UjsGm(2x?|C4hE?-m$6م&[oAqQx8 .*b%#y\˿|0?2y{wi/HȗUH7^di:> Rʹӈvo'iΡi%UA|Iή B67{dnF#|mL'"S/Dڨ09bU.4]7ƕPE gW&D- N:>GTMPBdx̸r/ÉJ[1M0esL98jT_ҷ)~<6 ׂ/v c?H|ѣ%:,ޙJr͑qWo.Poz6r$$W.5Gt-kͷrkThQ_ӤD5H']l=H,¨yXoc\Bh.5#*H/ъw^7?PC|d] Z ~P/{|KEhkVۯ^@,Mlo"YS{~剖3H}j026L<"+W)F^ʌa-{;ќ:}{ %M9{6⡋W'=2_h?`4ƙcțpws |4PѦں氜o[Q Mw0Xz*e3>H ҷe+/&4tz$ Юj4Jom|j!蘍#GI֙ lKvqao8O<\|ԶrMԱтF8QL3FLIVbxƒ_zLD8 OVdv.G(-/)/Hq@6A+/ "tk*>(: &$9/տ4JfT>YvBkr.& b5'3V4I;k} QS[ ݄ք6~Bod~˔.`@t]'LTms5{\.'V-``jYZ=rd0''tŽ ݑ-> {RSJEs %4p`eb沴R[@Ђ>s"a9sǶ$h_o+{28JX]U;(dhˇ~() t:6 eJ'.N]6jN+W?-졔m4LK4}3)ԿYO ΕLDVgreFiRXFbLԹXK.̢L4Й$"y]Kf"7e24 q"t ZbaP璏z a$f0!pN"lh$xNHT 8\XA֎RaZL5+Kcf)2ȳh}vyO = O'}%ΥkAo ^ڼ1x}7ۘ$%Q_rJ*TK;Hs]uP-3(@ϫ߂%*Q_,ԧFbM׏aؾ|0Y P*㥧絯 7`5a[W vB;*?[j/4,MwN8BT3X Nba~N1 HD&)Z[f1MIZr_yChJ!Zڷ 乼^Nc)y-$`gȕXfW)\F3N9v|qT2`1ׇT"%ChÔTIFbM"%s5ׯR^s3WT/a 2^BmQw>p٦*0^|^ernoS3}~st"}>RWk%)יxOs[wH8ۗЩSzTdOʭºug:$'rK M7l%s2uf.Bo .9It' S\;z7qd![pۈ$),\$ q-SIǟŽb WՑnŁnHX0b)4!&'΋<Ȏ+J027ah˯ZZEdy`͡-'X7X#fȤ^+Kf!ziG.wL!J1s ^6>/)i[by2ufz[/ Ȼkcq+0c2Aݩ 3 $_7 dr.R7eA#"2͍`g`7+@ r5Y8Zqr.^F@nlJ A3`'e?p,42VڄEM@:yg^G4{2bMYM$xfJ׶az1,bUf⎐OLJ6; FFmK})npҶg1LU5=6WL`+H< L7azhݜ+ &eq͐Ց~JZ c֓5B=+53/]!ίĥWGK+M8Ln7CL5CX= !c_ #<1Qj cc@˶@np=Ns{JmG}z94!tC?ƛ R ϕ{{3MRHx.c=-$i((:GNx|vߓGac/TX EXgvC+ ޺ m`  B^EKPj~0'LyJp u`d?pu>GgiqyKs<;nNW+ }+v%n3d>zu͏+q?g@ i(n$&]#)Q1 I޿$FE@_J eQQ%OA^X ]cZ ?5i#0@МJDSG<|# ɯ#sOdI . @:V{ l 5zAؼp)޷ݥ?Kӡkk}G''&7 B],PVYLHRd6ŭLLGX2,k%hg Us6WMx%s m p4:XĦ]\3_haF8~͹u|B v]#f닡Z2w4C@f+*}Δ 1'PݎMyis,S4ds(eݵ>BхSP+qfjӗLؾ;,gO}rf\/|qtҴ ^;52;,>GwN` t:=!"EeG} ;*u j3Cn~^Y+=!lIs)eՆ)U+I9.#i{u"$Ap"~Z zN (:P]dc)rȢ~ޤ'A  *[\^߂UAZ3DMlZ(}!Ш|Dz\kʠ텺DM5=*_?s"IT~7]&+ qz0ϾV|'CƀǢei}*"zLT^5Yw`ig&fp%}1ףIYyzXIklu{Q `hVpyS&Au!BBFlьpg.d5ƽF"6Ѝ#u LGg̛x_N:F;l@6݆@ǒC3}XA<}%:bI;/=uW9mcaHz ޟkvb\_ a;U wb!}3~߀;A9:Q6l^,uXE8^LfQp;8ݟB}]rέ/p [KvB$oL71k{i0`3Dkz>\cKNv/6˿͍//D+fʫy*ؗHWH0"]  fV=df{%wKGi[i 'nT0 #:O3#޶wl"n퀚+ĥJTȡ@:9XݓV5$&-,*BIuW {?^[϶Zz5 /GG-69޸bJrr;Ɔ O,l/`1(i +r{Ky]Btru߆0*셿# v5r've㴽a7x&l`{?ҌhbyL^r l8hΪCn㙁ڄ${J%./zXHo;cn bND+g"}NS)~-[C/Il 8TWgDJ P8Q\hIӰ6^V)w}aX"7KnƸ?lԸx0~b+t[F&lWp!A# >OGk #rW3C%H38 r= PC j J gjx[+<|#&Q `ZIF ~՟_{5qpU\ &y(ؕx(CqL((r.+::ngCxnqa,©W"/ 9H?^CpDijo@Ċ0ڝ4S1`HTu ~P`M6_nb?9t̗JT`>ͦr6 6P䶰u%Q{%'0[ =!{x*g 72`!o#3+cA˘Z453.O'$&7a<ӻ'#!1tZo^罘T|z~H#8ߙ zQ**ܫ*dqG9è#$ʺN|zuZ8a&[2&?vZt5>+R{$/q)2C J%Ϭ<9oK[7K3PDGbG>[e`dDpgƱL/fm6n)qlF_Boknl}e͗ExZACYBxul@w+tWb@1W/9f#mYm۶m۶mejm۶mkν5+gLDFd/")+*q:}Ȩ?O>MMM)"H0~^`I$rF~%ivſVɱr/w^-{pX3pg MwE2T7F=.`NF]p+cZĽ*N&ZW}&{yy_sf=AByba=-/L:[z{D<  Gp?&ߢ8i560M[uk;S`jM'm4Wa st#$K¥J]h/I:ߡS$קR.f0^;X<Im :e2L׏^σȇ'ne5Ff3NM S8!\wbLǦ)m87޿)§)z?I0Z s莿W(ҹ^Oes>tފ\ۧRsqJ{Z`Ki2L R0/[H,5ko:$ EEE|/;^ ?HƔ#aǢn_DDTxQ"xҩ}-{95;_Y>LJ09+vh3+S t'őZi!S.s1amA:Y/SW̜袣n4ABsUk7=oy~?LF`b[`|Xf룲 Y#yO/vd8(}q8׵y˵Y7-:SN>NpjɿIR ]N4+ 281yivUqĹWI^Jg&†/K&)fǸdnՐ.ON ^n ,Q4m/ y$_H鷐 C^7lldgfo|9սޙC~clo$NAvoڒZK*66TyGㄌ}- u]F6Y⿧QԮ-^! Q1gDG ^Y:ݑ޷qYt ݍL-һT)B/8:{֤ _Nvu~J^e}Hu(Xd=2H5^: sFRj_҄] C@"͓֯0kDw/1ByO $l]}IH΃"d([`㗃 ];A 8^(QT/)r\G?B^1Bw}>W>`c`*ia(:l,pa FVG!U 0U _ *D A~ZRS5F%l}:Gpfhn!J|b'yYYi x8{E*=+P޼m\ޢX^5dRh `J􀉌gioO;:c̤)5Û%80B)?{TpzV Վ[TSeT<r|;n.Ȳ:=9!•̶8'_\\lQ#\" >^@%;0ۣQP[yK}?QQS~( $x^~_>6cu5u x;X:7*?]V|5Hk pxN7_^^N߶-VmbN{~T%S"3 Yt[t/K {-V+n]Ur ܜ[:dV~^nUma-젲Ky?VV/YAW=!y\V~0-81Qʏ*ݒZ*-Wcr:kBjWŕ| vHhSۀc2ρ.-#h)T gAN(5͙ٚ˫X Bɫ]!z++W&KUX+P<זzH@YsՆr'XpI˱淤I$Cł;m*)! ujvV)h*;4b_soYa?eV*Gy}=w'y?Z~T̀#פH%B(V:("\6+Μ~K -eu%O{?Q`jzL.TYo&HE_ZB១n o0ͬihb,׃%cl*-(_O/33 4Z_rG>^|n\; wi%~Cv@t1WȒU/6 d3֛n 0>uR s*UBg)Шf J9b굺,#Wg+pK6%ҿz}_/ח~$&Tmh=,/"QҮ+:qJ+sHKI+ѽӵvv>v2qPf3*mi0V ׉BʈOJ#xg:@E gƤF? 8ñ *J3"tx wdtjB~AA ouQ6XknJH\vwa@ wrS˹ 駐Y=9$w$"khzRQ@h?sށ\ѭV2t`Js?^P@6?񽾯1xڷ *q鍶yI͛wcԟ r(W21qqx""hIR1:WzrO\̚}q) 1(X-Qc)SsZѹS(~++0C/^?-x9hzE"3VЗP8}#McIM;9-H wJH}U³2=@2`Ds4ܟe.?ЀJ^BE$}vVUУax^YN-ދp67#Q j8U VJa|G;Cj{DV N,n`)4(iRCIc# mny#b1'&DIF=FT )B)&2I".Ʋ@nE+lYfk(qtk9r+cEt#"v+es*Y!N/ yW$G&I rp6Żxa6B""쮪:`ya҄mPVG(oГ<nfm^/^ݱVD1s~T8͘ 97lF#!'' D9!c_ְ'WW&=yD#tNw>O m`g9,jqui-<rW$؝a9ySG:K[ND'_(b@tF#oG}we}YLG3"rIBxx4v6>FqH.⧘A[BX d$/AA*4u"=XZzqr6yfXxfIͦG€w Q:Ig)Z$(=wd*WBP4wxZb󿽍h2c=u=ykrtw}7|x;_z-3j ûyu}o_ޜtp=c(¾Ӆ}03޾%{L;U}cg0wq;6r o_|ԧໄw#wA݀H ݘ @Vq)cvr5xYy^YL6e@lL٦S?편1\xP|K:"}"IwFI!yۑ,ݿ?+}9RO{#^r=P*,YcF$zT 4儘FX_ zc qh=F b)Ap cfwCYEB$s_\1 L)L9 AK(qvtPsmL iU3.[m]˙PdO{mx%0񳾍XGr$dCL*,)eUyxzZf.pEs3Ց\ͅ-V*sEnK7>Ǎ˩K.֯VoՃE4KtU@ԑ2º%p}j}-9kI%UО"T2봷OVaBe =zDRDǒ?$!4Qr*Oz 43CA!.A \wHս [2]D;5;d v;|NQ9HV>Wv]&.Ei5;:U~؏+W(-TQ !dMoyRGe\K)e.*1g)8sC g+LtY\hӌ@miJQP Wn/=ei\oY n )s?_<WiYg~61zrF j$ȏ;$tQ{ |";(4 1%ZD|E-Pc) 55mGw :2EC2㈳!EËZ"V)0pƧ.[OM}Cl~ޠ)3%T b˧KK *(SBl>^^ C6,9O62#nlb@4+Ns5԰d91E(_p<n8i| {<+reb&U3 Cg IsJ 6O)IQ%)m -tiQkD:'Gb Ua2?5k]ų?Ȋlgbԛ['قu@rlPj(3WI:m&.fTOh11Yo%J]$]蚶2]Rx%}u#<#iԘj.xuߝm= b;.BD­iv7 G 1{+蕾̒!\X+#,22 #1Z9^nVI o_cI9ID|U*Jy7u$CGVw7`nGwaY.;9BWxp|V}tth 3_&mZrUpr7#xXgoP@- 7 *XIZ''Vr{6$WK۴3k,I%n,wܥ|! V*N=u/_ <^z^3'Ԟ8/|};LIϡ$4{v҉oC#3q4StሧTuqדw!Ai4ZL+ uD}^وA0pM`2aO hr$@&Z#|ieFFiqBԡǐIg:|3`Bi]!17K31QٍR2TXR[F OeS#Q3#NAHՒ0cr6{?C(\(jr YJ"eI%t.w#A&F`$*'tha:ǣ#BOQ?Aog tqx .\J4!b 貌: F# 7WJcN \Ȅu7h[2;ԥkN{XZ,ϏHٗ A3iIr X8Q-ؐH:&WCk Y (D".4ˣW$˕2ׯVUbՁ؊hiMgT`KsΦ%yٸĦQHU@7} em$O}T]?M8l¹+4mϙ~"TXaѫUɅ #4.$]Z"!]>cfQ[nu {@Ykŕl?Z\c"m`WEmn/'dV 6m0#<[d 舑@e_!a[NOQ9~ .m}+[S}'dcVja%D*Ut^R˓} X!Por mkߴѦhge-2+t@{RĞ߸סvkAz}L&1_gߠg Ġ/9P;(M \Or4()PE(͋mC#!f2Tjy@vawf} Uw-f"身6߬P.] %HJtXhV)SÌ5HQNkfglo{RnVk:l>lwNJbvm&)mvV߹EӺzR⨸y.uA͋lRrH.(IGatӵHu&l^a•(7o0\lP(uDopt*XCW^n0Rً1ν>6u{[1;FFNըa\<(QeζLhfT*`]/o(w@O<GI9UZ( 92 8CĝH"ܕ:她oNkky("Y0F)Eϱ{V+Ɂ_BYal :p) ޫDd*j"FqɅ)(wø'.rQqtZv- [u:K'$/~qQ)!p'C}K9,ͧ=4x͠CKq SoNr06Bq3<0R^(n/̤Pc2aў5[}v;O4g 6Αx U"#7lg\cI.=pdDXo^{?ǂC,$.$@ 7gy@3@Py:tЖ]U]IB^/*kj'w|^/:uJRaTf58z&AWߞ[3B=^O,z2_*e=y-P+6]\|o̯s/ù /5iąNݻn0h|]DPMAj1i6)_ Il:sxZiQhzZYqQ @! tA 7e)[) vQf NcZ#P iKKp'j22PDt/Wóobtہ*)lh)Pi>2Yې*+ϱwD <:<%B>+igOU<]u+bFX^goCuҞ1B~#qL۠FX!ͫKf[ULNj2L1'oZ5E96%؍abg`R6c2WCk4ig4]T6ox`nIrQW0$1]rrCٕN/b0-1D.#oLV vUGXNpk{FS'X A|X8NgаNAIH(y2WWSVmk_q "=ܫ% !Y%O~ݛ0Jm.&>H?cL$Pl3٤ydS\l F0`H3] eꋍoX@V8aQ^P-R^,[WM\N}l ]Ֆ`4!p1Cפ߈LkO_a47'ྏPH~-i||FE*ЀOCyT{!hAݰڔ*+#;Im@l+ {AyQ!@: օPܪY-,4> :<@y2w(<.`̈́?ѹ# 99Ǟ;3 -LS{Yz`u3c@9 $;H ˂P*ID"gٌWh5Fmuq^'\enpTddh]qD[,zsMe{ߴxx/-rL#~*ANB*+o}%oD w34N}E*C dn]9OBLt8)7  ~c~`CboDen^ WkyMz g8u~Z+,*DƦ#6&z%ڔe&Q/ItJLV=0G%nSV,N 񠃭33*JbLVGJWLK_ ݹ N?_~n,Ϩ 8.u*ٜdv]+2?މ? !癫rielZltw8l?|hg :A޹5X>P[ڑ\t=v;nkB f g> )7(Cw0|s`xA^lh,; J(ܿҦ{pc!yc}y77;:[zkK zjZ~o\;q]jW1[1],UIt*Fb$.q&_ ]6%僊8Sd/kf"8)|5? Q٪A 0WwNd"MuM34  9f(WK!nɠz[ǬV3)`sκH KDb)BlW@x V[iO\hQ< d}YDf)W]KuK[˛->TĚiO!C#[6_h-ub 0Aii<:qL4]Y]E b9O\>,rSaWfM-$A[6( wO%y;}!@22eL$-3b.e&}2\p6)2w~߀K]$v/Ļ-hy܋% 6-u:TT*F($VZHVW o>?O){F^.=^Fy| u=ݡ,RYϧxF${TSs͔pA0Nۦ*ةS̲wN&Pv߅W+tlc \EЛUEmib$VOEwJ#elKKk@cLLVၹۭiqC[o`ӫ|ۘ ]A81Z *˲:|H,VC`Y*Ÿ"F u1WgwlqTpcQ(_|z)`|AN10J<| 83½dSTd&͐2MdC[5mDZPojI/8-mh n{uS ZjhB"r}o k>ң߯J< XwGͱ[ʕ)R,UA;f:Q4ji"4sԆݬGO BE=XpI9hmN d6b.RC,ϴ졟]2;'n.Uj|ĈdSN޵i7k-|sm[ת}|vu68'_"2 ׾UN'6P9AJKK/J̔21˓O"Ei "rMQƹb[`` ULH ~RApHv'm^ 68|,-<\F?֢v̭ y"̫(zXN:I3(d`roh {'ތ*=s2&6 e5 yWuklv o,0W 'CPqE$!!5;8gqV•\#σ _}vVƝ.̄a7v4v,0/ӋEazxx.Pt|lIz&Fu 41 6+dJA|j,#CrGC$dJԦ%u!ʏlb"-'X)&C/xT_ӻF`wJ4G׭/)SY{ZIɰA|+Lj59cI8-sayaaB{ ʕIkSf #S$uw}PՈvƂڜXc d5]6GE3^-{1em]UfdG5cqF􆴖X5;S.PiEOCFLFPJ2l=gFD?k<HP=N˽NZuAt.8|)hj=}6ﬕ'QXdul#c=rLIYJԷ+ZvkhQx^45Xʜ!>}D7[eeNp+VKmeSV+z+{tfk2[<.$nŴU)pt9{N Wb8s\O6vSb6a"I (dT|A.]MSO 9:n5$ǃЌ6S՘ ,B`_g#Q/|)R$=W2&oBtp(r|rcr|Mn7E8^wQs'>Λ/~;>k,q]y訇 ythl]&|0iZddOȸ` O1k,#}~^b[Se6ժa`d¬_ 5?|(^9lsO<3'/c0]hp,;s vNuz~Q#"(WX^Tܥߟ9LI]1X'6ӫ\K!%b&Ϩ,Ov*!}yo_j~8~wXi }ˍ]i/={ciC}٭W/Zȭ$_a$6 otlx"6~FQUu8t X57$EA-Ōy;$}cFƭ%DsNJn1]QԸ[g- ӏ-8tK1~#ϴY؟ Æi1-*3C']hRR28Z,ӆӃg\]VD S|)a< :?-Bڊ8#܌:.p7u|hDVp9Nvl][Me*0I尜S滙LOOzN]{cwO4tj{(,:~Bz;kP.1qpp#ϟVN0u@mEN]I␫kCާ`N :KTv=H yq2t2t)'7攗A.~&HѦTM蒧۳XDN^ Dfu[>ߘ#q9C N} qaS#}Xa KŘT!ٞIyU8M֝m3Gӕ܌gxH3CydM'i6mѱ+?;[qNziZUM)P\f5^JOAՅ>TUJO`\:i/YoAr(]ʋS+nN%.HJ*-8[t(WDdtj(ϊ :PLM)mcΟe-qxV c }XG:5۟fБ{hvg"bf4J !Wq:w'Y2n#i5T5 (:Nq`[lU}h>LKk[' k)]BȺqVc , Y GecIDUuS;> Rh= QPÈ_010vX#`)6DJٯ_H~ºnnEz8bVQ6BEK\UJPu_Jnvw sVݢV&zZZ;T$쪹yl?vbO>;U?ϛ*9c2\p'*d]J=VB9g(O\ѱGWM Xa.ˆ>/%_ooܷZ;MkTΓP_DIIK.[ȟYN]*Z* viC+By"T4 GmdAb<)+;bR|!bNx㘖g-}QUr;M; zG1!'G$Gecj^rxK tK774#8;y݆[V(?rF+]BBbr+;`&MLY|b-JM& UᕦKɓ]1wk"3JAȽ:|eECyj[QjËb E'"w奐K\ґWJ9'(ﱽN߫n!u6vQbdB3:>ک|땡 )aP +VTpx a#}}7DHK3UM^ż*zxWy/3ݞnmlg +RfuqAjsט(Ȇ}IٲVU<~[snjܵNM.#ٷқ.a"&soO}/'&|μ/¬JXj~H.!}wM?ypac|9lC y3|P>iW$\Bg.*"ӒفSuʐf|=  78:MKĝY2J\*+ N)Cgm44B`/*hfH2޴PR/?T"ܝP_H&U3?p{P}R =ƈ8\g<*BPNjb44B0W"q9,17``lj>Mk7z 8 nr_9¹p-_ceOhl̶Ta9YOGǤEY1̸'~Q2r|w{v؉~Zgӏi߻@X1쿿yp'"p5٘mi¦j=J OF"n*8;Fo̞?]'#t.юbyE2Hd;K|;'4׾)mmubwkSsTTbȾ>J* ٸ7,((B*WSͣ**RoI|xbZ ?2,U mA]H|_2<`acnz.~Ԍ/UGއvĖ)C n3|'A}!(GdЖԂ2W3f|f3NR KhhD(c'#8WN>@ ǕOmoPx:ҋ۹:;Y:;R֔^B=1?lP$$dMB@7I7 ڙۘSE/lljO!?sO(zaC SKs*Tv1U#`wb6L?ibo31?<8ì vN4)AI*h4.8$=FӔWk&1\R+SŬީW̆hY$~N25c}M8\qY^Cb۸2ګPRVq%cn,s#ZPq)ХhwtB\)x|vBi 6n ٳWpG,7~^{I\&cD(]:~(0qZ7x<',CV'5a~jޘC)ōO6܎jxn: \ʮF.\8+W_%2f"laDWd +W<-M\,Pu/֏?2E_I Ӄ L,L,SL 5W|f20'- D `҇1hv׆RG443%Z-Ze?nRol^[domnyӚ~NOO9Ow%BCv8pC! 6hWWӕ | 8y^J{#dE$&7|_yeΔ ]=!B=0<@2倞d?8A؅x}.%.ZdyF}ps㯌¨C!C"ْ˸q2_?[oum1z@:!(T,a.bh$LOiV.i3E&WCNۧzleuyHV+;2gѵ?JL5xF d >u]f;ۏK|sNn۠kuފ's) PnK>-x']. QW[D+y@ C~(ƴRx-X6Fv*3ʍx'dfAy`-Y=$ven\6f9;]]A{y愄1?\HoLEy4x$VB _.Aݶeo GJ`I}umc*k*(i)&fNL̋Kەr(ᲆ$Iq&bfzyW હ :¾p3؟!7?ힱ_ 5֭!?>Kd$پp_\<7u Orw^Kyfh-EaQoh]\nٿk|~=1XhJQP8e`.k 2z_c8| OIFX.a)&.r8rymyf=X4gorgyڟmX_;l?`8=ynG씜}]$i:RwFNg}H4!&,wq@ q !~:lõ\GL 2zyO6 ndfFXj@|ʉ C}1H3:aQ j(ֶs`J>4fFqw}3@iUӕ ƵJ\AҶ$ɨKZH@UzֈemA;Ud5 ֦Ia #y+%.|pMvQ-oD:[ٰ1â~Z)'ܹ}=GȋڀTZ2'ż$hp&kz,-/A*F%iL.(w b{r _0_A2|K@ gM#QL*BiqXiѣmIoƩ5Xtb yTZF}cCQzw.p{+Uޣ*i[ʴ^۱(ЃTTNh_AHzyC >A %dHAT&qIfp OwiP>i#v*^`^7z '7 .#RZ 7[ǛHVc@yA"|,[$emHzEtcR-0*a!P8ege[mdMmM kB5I287 rϜ˻D+SPtp ~xأóY̴lgL ބ /`$Ajg_'q10I0tѳh>UĬҠâO:?=ZSHNM hy*G3c *pa,:8,kGD+wȢA (D7"F h?,^4>]fL5t?I;MR7 PT!%1 w20P6u`d oT78`$U жY3GiL|Uhs`= Cȕ&+RݬSa9(42T䜑I<|;Yj*]WSJ*\2]9BU1%vd o]t-;I-tQ3@]I'(3rRstLGu`2{p|jPI5Kud:Ʊ. !"l•( A%C.]E;SM0" NЏDv' y7pSe$m TJ0⅑4.(azLR)թP2"?b"M 3JF.YlPQ-G$1JXFyBzut9CH9 Gq!C5d;|.+C MT<~a΋gxux\5z3Ȁ'/Eԕf\QJ_Ư,#f|?  x"piaH*Uh2e7%l޳+e(:nQe'/k{ZǒrNyY/BWgg⺻LTX77y7=eQKmyYI"6pY\E;⯥N-) 2Ԑ CkQXX{a!m1MZ𾱽@ Z 4C[Z=蠦KN%0N5 Q`\KU.%pby}1iMmm ?8ERtRc{z`; Gd͚LI0~_KTq$ .~GO9z-mE03W8@KY[u=LE:E תeKJ߻Q+.H\!׭rUH}MdjcltdžE(C9[YmHKO p}{2-'RBiW7m%=K봸6-3<~֌~PjvbRc8<||T+'zw?k$)"vGw}|@~3`󡄲ؠ5J, E_]h a^/!+ew )Hˎ1$7 7Ťlr:#R!Q'Oh 0pU>OgC/-9= ]adr'3InnjꯉIas.}Rkji*By88\2b RRXtWo4!MӃ)2 a5)Iu=ᢇ&w儊r|Ϥ|3G82@ % (/8 1gCl>؄Tx8 ٲuJjD2DM$`GzA#`z 41ɎLP@t 74/~2_Jfx!8^vho rd+|Dh_uۘ$G +D+_ [LZx#>_L|>ٰ T$4zΌ Og7U3+h[ *A@Iō|d^kgrXTr+׆s+gNAm}F ;$d HaalfJ3t_Lt뤆$A;( Z OTj]z6T?=y<=W'QuqS5<.IQ/7[s' `H$bC E ͐RBSs8ހ;??ޏԔD\v黵x~ĕzpZ~劇ML&lN"%F~!Of5 $`5GQGI+&Ns&F>b&-3F)w+% *atmj*v `u[a^Lډv/+ "o`}݀?z1ƿ~aRE+; mdCG̦4btAvXGק}GZ5H3t)aNϙ}®R柩fKkUYGge:ktig) \y_HJHM|q1TN)ҽbƬ)rÆG_Zf KK*dOJahr J%,5U]z0]Ea)\2 O+F'ο8 "n3#wEרLza/U SY%і6۝x1[Mpȶgrxg'{"ACͭQ]YZAAkMP"Sz+ T2DyHC8 #nFT ӂBn9`RCY!oFUy~ aMfB3'ib֗tE>i$Ye gx{wmkqlfokx99*BGJ8 KLe`uĬߢn3(t⡂O` fF0}Ǽb OinZÄhYWf~8Ǒ@EOrj!wO(Vgob}w0򶙠-L4RԕRjf g?p% R7 ;;m@ T,vUE[gB^-s 0>6'W69Evu=_q3[cx_GIѭgPj,[+í5 x&Zո%´X$Żjh?B$:\T7`\9%J\ /ֳc~Hs}}q3'π/ }]XʹYJ]]Xpqrk5CRTq(ђiIc$0ѢI)D2~3"Y(ф O!-{h.aj957ݬ>|6d>A/PK=JV-ZVo=?fjFg*cPڌmEg&;͐zHײ[ʞ؜ 7M'V$M'QdKs J<ʐQUv8bM3*۰.՘QPC6H<{*l2x!hG7;U~?{$M#Qvt$Hk&ɎOMiO\zfGQ{5Dr]=8li!h\JPù¤eG>)Nu2 ̻͎TTTi0-Q.>>VT*GL]ZVpQk֨~?eא-TMP 7F2S$0wnI_B\d$=lZZЉNu%|*fk(.* 7XYhbw5YPT?>6 KR >:h卑vLSR_&]`+TZGzoyD?[[I`(=&y`z~}5p{n. X%l[Zl5=, 7EX69@5y{| Rqf)Mx\r6A'C[ i)K ND@@Y5!\̘؍]ݕ j#i%3gجz1]9e=PoDcvbiUs4fGUMLBf&0DjMuC^Tyqq{SR|L︳dtq~[@Yt-gk f WUS+n4UA~'ؼ,6i<5ٗD"b %}Ćě|tL6UU\6F#xͿ`v^%UuЯ|wKuD3"&v CJ [+|鹬`:bEvRa*=2 ?TqyreSR9Xd#3m9h9Taڏ5qCڗ 4/{M MF te*iZf+dOA `bݴ)Ūŀ0P9U櫙qemQ|\, sfȤ]frWd*n4`LɭוђZA-V:*bþՎfiid5:u4DWţir^@]qMH]q=,(+بr&|M8VHҘ""MMpP8T&X'"JAVI'bNS pUS#rWa0LPOm{Dƌr"qUHT|oG9.PzE~? 1RΒҔL/>: $m3. ʧEXo _.&Ȩ@uN:Ӝ-uA[qdo74tn+j@մ_VnnuX ct2 ,!~**V!C] ͗t6gWAW[eA15T8V*BoIDj8Yjx=2쐌|I&NQ]`v8cʭRiuj(9cހ%()gL^6nD $^] '1PUۻ^Me` dfz] ;@9N_t'EW`9(ʍ{[3I8 ,fiYzt+P"2=*£90MUV0\j:D`Tb즿ٛ΂E I JWR@YQDS_LuU?wvyz=b6azrE8kUmea?q9jCVSL)Zr,8P5.¹G\#ωYDk.P8V D{q[YGl#dV A4v<7ދ'6zo4}>3X@aݑMAb\TYXf=}kw[3Q;;g4SG>1{Q."}klktן]SP~=% τ6+&u OhH*3.fJC$#E-bFpC$`hpil|c ڶ7i~H(ewzАYo_@CE0"9EyM &#;Z5aݘn|S v;?|C t=Mm#Yr7őOp:1[tړjQPu̟AÇ{QEx" xG )aJS|floy %/19텽gpȏ_zkluc .F̍i;:IvѷEgϱ+k 'M B$֣ kj3쉋 ?u>#O]Pl܆Ksj!%'_!AlvNqaҤ3c8O8D5dnˢR"%[.0D߳iu\P̅2axQJTt_ܕ_>@Q5d`. =غبF7呟y&y_NW|uY 9¿&#BKKX#煀XEl;h!||v)mgDflQ2B5Ds{,YNL.3sъh/2&x oׇ1r*!o7]F0'Ҍ+ҋ49~p FjvXʨĬ@fY1Z4|dW&K>ΥIzHݬL33}y!r7!9ʠ)IXrVt\mx^v3'48C2^9~*4Vc]O^|u J w}Oz>L-8ƶR:Ki7Y[Y'^}.QX"DNp]c:&smI%\! UJL_2zܩ>/Q,:=+Qb9n4}۴*~}{bࣝlyto&%XcFJ;;[>G[>^~oz39\voA}h`B N 2/H Ks_Nw=c0ҩm JʿED95.X@Vݧ)2zO14#2rB#Ա}ލzBF}C=0iWw}-> O)3}24zfͅjֵ'@<ꇷ\G~eA>5l}u餖Lb+;goQ=c-x4y.%\?e%s8W`wtE?l pj۶m{m۶ݽڶm۶m۶}}޻߻w_3g*#2kd3berg|XHC%O ŬaWܫ~5 QQtn hNf9c )Nf̯̀O zGh1,GJGkW KٰbţV)tFb:EZVzhC{U02{+ (D~y, ?pV7IsK5TX=Gє?+-՚6inox85^$,|SfZ 30/hpßk݇uU%C=7967guy6H2ӛQ%i]FF{YAF6ԗ nf)$7G8kIT:F9k}ம=rQ1%Rq  %zHf&NHk@b!%1Ny U ǐIXoma*{o^j;iq? G1)hK..hC#آ&FSCO.)Qm]={6js>+Gz+e5)>P>;FV2n c{nddOW7=ǝ%+wίwN$:֞yJ#@@@v#+=vDt"e55ܟ/mוow8'įޯ) " ߍ#0^: JYy>QKKN*F&UӜ̋Z _STܷpT.|ާ$$DNV$CLkBab$D$4EܰbcI!2R"?$JU*ES.?zbW ibbjԆO*PO$g6U0$#.f?l/VC]N;7{1;1°1|Ԟ9ednxe^oTi3B~,z X<3Zt{#H0}!w! ";t?!tn~3p_F:#:NS&٠ pCUTVMGw^lDhhB{]~7e)rͷ9'搚>M/T`߶Uy7]6h\=TS:^ŬE4 /7/4&&A$Ruo r,}-uvhp?iv< &E푤󨶛;Y0M;bjbwX5Umnm0vCp:4j9aPf(E[>;0@\L71hVՔ6\g6Zɽڄ|ҋU1nz7WyPY4 s|κ/]bG#5#H"g!,h? Sg8W6bVuK_|Zs|ВjsDh'A#tPcT茭JLO'v@Cg(НŮe0S* b=dW >B}ٌi% M\*j+U0ɊG5Hvӡ=ynŴ\a@6+M뤱GzZ\snש ^NVVju`m6L~{{S!jcba!6(OJN}puIJ;R^{]^b.U+mj{d;vȢQfe+c/dr';Y#VFL8_D=/3p>2:?|O>o3 3~?o%=_+cͺ_z߈P=Ѓk> z_/tߏDZ`lʆ<#C O 9ޛQ#~a#qhv <0=3"5ˣ~ҾLg2_M?FbL`}'-;]w֞`^_5; }|M2CNG23xDmWټl#{ϣdwu L2\Y\//e>hLaQ|I/TWR萮"|ԷfYxInnC]3>ψ;|{hT e7 ZXCp T<7:{ʞG9_u}(e,Ӫa\˞[@ ߊY,>.>FQ6Q R FdzZxxP.rmC։wJ[j7 yu '}׭譸0d S\VX%r dl Tlb`c`7 e#wlX_O~ =^7D a5So| BxLTĨC>ɭp{W#'Zb**8\٣e|*Yw0Tێ8ul8]Ou^U?- LvXKxӦywf ~7Ԍ>v~i~[Jxy<1{EcFTrV{#-]z1]dsvvHqX4r?|*B;z7@˧ǃ 3c9J6NQ7@eW 㛻ȹ^cꄞØ:ݺįHt5C>>m(3']-kz`3[ԳȂ _ z ?JLȊ+-[ded>Efc~ցD:.w?--떭f3uڵKp~}bD0`Gͨ=?0tZP2y0RTtpSo,6EJ)39^>%=]Kt깼&SP_m/fc4j4.^wܕQ y-1\ѡO񧃵y>Oq4-  N^OU1oH M6h7À8'/Og#Er}| U ۞]_Pyx\^;0:A$nh@x2\Z`Zx}՘K[e @{qF?%]\1Է j˃ma `EiKShwf(}n>~>h0^d*U8F,daBGfV((i \Cע{;Z'r`  2)Y~r׼HƏNjog1#v)gM.+zȝ*(ECPI9#MR&O!ϖbqֺ  !LW>PhJaO(K>;hW/lY-2jT.¹Oӂf,LF!fO;dD;Gôn4i3ejr Ew.ơ-yqK#gp$$5˓ZfD[f깵;*%8A|Q0.V&:^Q3aWZ7"͗v:o.[}KT &$s1Zc~M./d+ARjr'GhKu[ !9 vw_ . B)_ȊNRm CIzꟘI~6 CsIVB^CC4]ը.f5NQpB%5Y~E4n@d0@gs0)$V"loA=QQvldzB ?4?a/-tMR*KOݝdmQF8CN2%S4QU4@ ;FH‰ f1W c@3)9Ђ=;#>%qpB_+!oL!"$c-顃^xvgb}ց؛117ȧ [j.Ӛgp=r⹟oջLR-7G #?5@7Z".-/9 vZ[LnCcFx( ACf֨ɩ/qZc"?1_sm䭜ZI R坤 rzw>Ch 5ޟTET>A]`Yd )J߻IWl7V?=ݕ{ XYXX+0 o@LbdoA ̌&\qCi'Le$I7}bE63J[PSg/8#zod2.LOlcB#)hL*Qu\x<5_׏Lv^2|xO^Q&;5j:^Ș=A|L&RNy9ҠXVPNW2M'q_Aqyu{vHm_n-vW<33}AeS;RݨϸJ+փ:s4Ġ4N{.WGu)UYO̕)[wS g4OxJ]^6}R-ϳV ;]"^ڮܮ vUzq#.}[c]/YoX苽^g`l?U&1(fڎUQɌ=Տ#q*^k=O䶏g_S;+.4ğ/%?5Dn*&1L/bGنDG{rqያ[to֭i[cA;!-@'`/ފ\޴H>Ȁ:^j\h07u >ɇxcz`@w|Zq9:5i2P(0YEW䘼5OƷs{'>YC85TEZd4JCc!Pc{tj !9)oSW6"(+uwUlju"[mEv v}:=Nٔ?/rAkl{ ~1[\_Wφ-LǢ1l%Z18Vg;\gBc4_WnLc3GZ%ZċjMUs/~P}$vPDݥ_ecBz:BcuٯIob9]nd$Z{C Xɭ$hάC.R(bu3yL+[b?b:R y׾}TexRdn% f^nlNcjQQ2iiru0՚D~:U EE~1Sc(rъ:3tHkT*n9s'@ %rGyVϭACɏ2~YTC4J Oe1m8ɕ%|s<ʙXǶ͘,e0QU2ٟP, Kĥ`3BFhd *ClxRI5#HI1rrF"vLA) 3`)r)?FK(c4bš8RSS(br%RD6ϮҖIF+O468 1л /#*J@p.,h @Y^^ȔaPB ^zr0"?kd~NQN3V h`.~pNKD^+@? Ȗ2{VwB^4;>7vth!/u(9" 8sL::GQA;0n)b?gDbȪBaTDwvrqjkIϑ6p0XcMm~rTz̡0Z,{]]z;.Q"Y5qV t$՝.ת'.O u$Nܐ8T3fЅmF( '1ȤX"o# LRMHs[]1P1'R5 %^0iW)(wou~m+Ap- q/[p:)ΟIHF"9Lղ8e;ʴ;q.k0ʜ~lHP&-V\DXх!s/ qy8K\PzWϤƄCn}0S%U^X!)BDg"sH4Uxa2AYϭsb"]i֝nAȁdMIOGX ,¸1Yt4 q@+*F8K -uq2Eԧivw[5<[iy )uTH!u -O!(1%0ߓWOzݞ|22LC"'Md#Aa允qh0'1T(1+N*O2j8=1& V9ޫc 낄:9U1iT΍ƻ4N(y($}4k3%,Yk9o@D&G#SHCkX-CSqG\y0 spS6r)NW9J4ܠڟ&2S +tvT&E=?gKq% 7d6l6) L얿El4z 6Rlnmux]CLmVw' dsrڪuOIG*e+:%XX(,=\I;<Сp5'\@SXĺ$]dMOjϭjIb6>ȄQ2<7Ɛ@ĺ,b{DG(#xDĖ9`%/Ѳ*-1F]e9qE.2v|N*uߵ192s.CAwV8x4j$`4*tܭc81.U c9ڰ^2א]Ʌ/-u Q*+Y}Znv)+7M-[0ʦ]!i۴na(3GÂ=j9݃?0czZcV !(V×ljgcjI9{lM5#5ꄝrY ݸ\3|v:jǚ G=f{,طq+Zj.``ए}7~qjwk,n{9nm4xfn5xtg-k9XWvvH76 ȏ7weVu.|?XDrOŅdlp\{R(q.2}IF"jNrfΠ&ǣ}/X*XD;1}u ڣbelK ꎲc+E,t)M 6?tAfn WS8eQW6LHu<wvÆ4yX^oNFN{>zy:!4;{̒*꫈$' /WG Fde Ve.aŹA3 )4  NИkj8@lGY9\YߠR⍅!h|Y~Ydv=?^nkflCuZϬ2DtKv[~Pv_}$! |@?vjNKA:\}=߹()H}dtH(iyNC#=]crh6փPgv7Y8"H7-.9>F&yz|dC ZIw9#2D=RY%cK0i]~ kMFCݟo+nK *\qIIuh _56HHܵ!sEΜF,W2 El7j,˕Ɩf sեe%U@>F_{]{.j|=sg(!Gjʭ% (6B[Q.C5|=f t*_DG̅^Be~.5:0 ލS\$tgOT "njV Md+Z-bfX/:+Y H'!*罢 $XwFWIXFGbpu9U"vfӣGK{[f鼿oU{ʨ%]oWSh]a ~4.2J}v /!|4J5ךg$O{;E9h'&uCQb-dÈ}R eT[gZΈtX*.CΘcS3 '/ʓ 4^1ٲA ECT$Zm^ݝxh{hdt\dDلFmo:̨vaב0]w `s1n,ZNCC\$c]΄#-6`\SB~CH~ywsɼFV)X}}6fɅI`0zqF+/`x;F-m *c6\._{h#17O`%Q$zh1$jc _41&<٬"o}2>56ݢS0 WLɟVK}{rLqS>ϊ4G}Mpe^]\b 'jͮ~uS̭˨IߡRiƦыoqg`3 ~X[ ;z &G>9.ѧ vhRJȥ;>+dl!=7U}0Tކ"Vv"e(@4ulDŦǻNE:X 4m3,&UL|M.;INm1LW 鈠 H|=Y\/:z4r?pV14"n>jbqq6|.]R,4**i5CteVkQ,%WKl=3zB.o뒖_^^L]QY~~aPvj0b@1ucT!◛ (|64aT Fz{iPn!T~[_N7^V Ry%zi KI|~|} 0%k:-1jr-ZډX麍_EuoڐAWX6Pm@@ooo.%ͭז8k3j,ZͽC}^H>v+kѥRwBS%cUFNAZPYhè8R pdsJDAvT r@T @ PQSap0WXl: .,6l&%|lᵼ.02["&쐰2%3nK=@2.KVKo 3 4ns2f C]F`30ԏ-{[q|8 T[Ǧ&æTCW/wIC}W#0JR Xmg40TX0rhG6Wva{}x^BnqF;9(΂ G G|`5spBi9~J`+8(]ju](M5%O: Fó;YR-a9u#qc"")!k jRERRe!g_7 (;MnmPoV=|3KFlX˩t؈^!vw>bÆH_,-lhscKgB)j-uh.W_,pNJS;rk'RCHXXJiW%eMXol!hUۈv$0Alxp(gu[bmlnd7u:Tx&x{nۨjVMl5#i6>O|Mz[quyOTٙp`8E\p] B(WCr_n㞈;@m5. _SY n*uԪ\E5E0 8)T [?]`wNoH7F}p+91} z5T@H[,rn7 >N&D2e,Yt6+ ٗC^j"A~A- $*mUǐ ̤G-gF/EoXBu;tC:WJm3N6G#4w\Eg},u+#IlT߅R\ )bNVc9dAf[mMPH2qxl2E4Ĩ|ppN&@ˀ F,|&Nnǜ׺?) y"RE9eFh|=,Q%Zx* zs#Y{c VNu*ZA]U:J }|ٹ*ûhWS>o 3C_ f  $Hhl}Hr\a$sy}8 } }Y[40%HU<2(J$W!cծ1@iksQHONNwZTlT m:V=9KI%GD>!1QKh!ddQ4 0O@-0A;EK1Ϗc2+SFP 03R )p2j蔎 Sx̎a| Җ%[f׌,.MT6VèSU| Q8χfl'ͼm ]:!ָJo%O#fXN#-\̈́ts6Y'sLeWٳѺXGѾ3MyFSK%i:!9SLtkkY4d%Go£H <}oFjܥU6 ьf?W+xA[i.dO+1ч<ࣷt A|d5i!ݮw .!{#20s>i9E;`x8}b.cuCcl S5zti9t8HaEv(.ѩ[hZg~_RP{(L!!awFK/]`Ξ3_' !->1NBhXzCTL}\N88I%-lLMx.M{=`N.)`kFsdhlKcIRCfd*{Vv M, p!}=p>nӾ_z3!OJ{Bhu&F\ƂNR_-T]@<2l~תԪ`N9,9FKPI{Eگ9:#K멄ƔjʤQ6v=E<84jklA W9k*|? ឡ(YuTkZ7x\Iƹ,ݕcKv."ҎFoݙ NmUeTOQDWL@N=*`7zۜQ[*KW #/̚/ n\\L_ס:Gi¨"ʎ #:WU4M9Y}<B8^Pm`a^V x}EosyW&}yt]M2)m2^1Z D?F~"u&B_07Agd:JISF.8;}u%@V h"œ]\͐ kHV3[, DIߙ;7PKTh\W;H_lVg P܆YL$d&I'؎J;w'K?-f8-w:+7u& O)J )%iIŊ[Ŗ:Q$4yxfy,+ m1R (Q\ Fa{U)̲;MStpTpݲ^-Vy) by>J'w\OSÛ6i ͭ_ㄦj4R21\&͓q U["ON-qȌ$OwR:M$x}z* 6ojݔ8l r:6΢pیI; 1+f|ĎoJy\[uE\wNgpTj2D\Co'le҈˔FؒuBqħ$\%^ JwVx" R - nI6SaI#"tvhWu?ooFij]'H_l}˟#dx!|wxp>eGy/U\qUx80t7 'sD)F~C]CkV$ ^AuEjԔ(48TᗅJ[jf [{}"  uK*C=OYz25,OXC2;MOoTeU({(\Wt8ls<eA3w(u5@Q&{)aBXأ2PŠɳ!]!GբhVzSBs7g(gh*{.?jh^f~rJcY8qTv܂Ecs+I;Up' jN +*QrfBX~Xg,~6_4cR)]de)ط.uYSt+­Kը2p= JQᗡWBk ưk Ax+:ʶFFc j K-*hV*gfWu1 =DF'U`<A$K~$* M !y]+(7^}!5r\.]^jySMIdMPw?0TT\!/@O_(kdNbrm[ý xxzxxTXH2idDtlN9QEؙ{t5RzsfiP7ET g;r$XB90dIj!a]&lk:c5NIW8 Xjhj ^<>Pe!+!4C,{222vsgLH #( lQ=dM8Lԉ2 H|/^hZ:7S%7#]$yllm*͖bD,noiU0ɘ\XO\B2 2' @8JdUlŚsf $$1*cEg*4R '>~P1)~J+@R־ ' ƅ-5fX~HE?=>d|G!3o{x+=K3}/oD?{?9o~x6/Ȕ>9`sm_Z|)<8Ϙ*A !߲C)|Yqл;lBl73M6UB,|_ES-DFh~`#DbiJ#[" ZgG"V!r֨NB$JB!C\ *kUl~Ž"[=cl02q68sδ.v,[@x_t1L8:u8k"iǐH/tm lǔbu P)Ʉ4.hJPu8{43 BT~ddQl,AåE/ϛS."<~W'ɟ X`U70$<$!Ywm.uB@Z!hk󒴨 }+)czf-=MN(XUR1ъnչ{öo<=l͎Bgr01ΆM7l1' ѭ6pq=A'2NWԩϼ Y5<:n(ŬwOzB[[Xupbd$h5F=Eh L1yv9rr0*!cb/"A<ïo^ڧ $&βrk:Ӻ_?gȶ[ ?~^-y4'Dp^GmdQ\P2}߆kI|O%.pP@ZNԋ'!P IR_녤NShQΗhgٍCe;se:!UY:N2K%aX)9yrٿh u%*<¯0y@xfaUy)(1Ly:t2ơ#}|K:fT}fO8aE &c +E|^ڒZAˡW^q&y#*$3tho=ﱃFVюf_ ./V=.l!u\(u7_FPRQ}L.`j:mƶb V T:[Zl QHI+c:W({R cN;jmm+Lզ6jIhnڕ2.Xڲ\ؕYtnLSe eim'"%e9gū⁼;1 v<eVW8 rfVTfڤcsڧo䃂2%i8(}Le$3JJ[b|'Ĭ4zP}q2T PU i}SZlUx99>>N_O+)1Zޚxz^l|~_7B<]\B+Hj!:e"i7𓯊-Ƌj^@<8h5{ J5l'9^aj=J2`1axy y"igihw^:s'Ǜ\Ig0P.tEw ڿ\*6vC$bUg$h—awF&k ԵTQQrv:=^:EW!D7ж˕q&4י%>/@4/c_XY*ydHf^Izv8'aIrl K}8׸+[)qK!\ iVH*͞W]Hwe6KhZ\(Z4-a3K?z`L΃N#_sNVCG uY54{A4s"~*[pm tUڸ2 EqKsҀԀ `w,m/^8AX^!!f]Z4K, U`#^7NKxE+({o˗FDyFg=&N*ej=H (Ǩ`ypIh(t{,q$8/2pR)\oY)\YP_%K]^PkJ[i@`9:lSXцaٵqymomoގ~` x9Ph3}i%S tBmR_m}-I/UFu V2 5Ͷ?}͢}N}Df03k+ U0M8+Ӡg &|F6N6?V,joVU™UxDt=D0_ma3B<0xy1߁/+LDSsm㼬V>c>!6-w-=#%g _ +'3Y5SUmRpک)#o)ߩ^VY='&FI'7p[Y1&emU@G`8vG(k~w5c QZlUK˯ٟN1y=[tg{uHfwFf{z1`}}$kvNui$s{nvNWucmZXYFk|8[4$}Jzv/ޡGuwM] utOoS^}e|Lyv<&aIV93E{GM9%xK*-]y֐PZ=E8~G155ge71#_IsV|&&qW+ i8G?YYYwWGhy ,>=gٿG;w[7?#otf;33`wKn+M+'bU}`kFc`Խ0VX}XVHā$ɍř~{.lGX?od4IS4ҝ~xcxt:N.x^5xV5ܽ G^l>u `pV{} ^xX_\`%h7՘;@0WWtsPT*Y:_M].봕Ueyx}|@u-IHJ ER#OŽ!S+!`'CK{`v,N'[%dꗈ@b!ݯ ?t%ÍY,g`&7,4'y:~dİ?˧0 g ) s,jGfs9/h?\z A-o[Ңs;jyDjFU~ )2g~5Sd_-$ 9}Kj-ċJmmq-h˅4jeƛL=L4__C [f$\ך󗛫 .&äkTV\) κŗuH 9 J_{EngwP+T&Ѷ%} Vz_k~j/ү..".2'jjgi<2S]:9 5l(%ΕTñ3*!c:{:ŽqϱAV} +hq,_9D5*Ȧ5v%;H_ܱ oy"\ݜЯJÈ]</A{k|OYqfu6ɐmjS]<账'Y6ÒcSCI=- ңˆWi΁^)\)u~<1KN6 eM byL=kQ}hFY4HWˆMŲ6BUo҂Eڞ߿ɱ#v( 0DfE0RZǨD0GE-kJ*722>tu]NYf 7!yVH "̎7 xa!K3;H?6xȔp0E&? vHPNbzpaȉ:d~c7"*)py?Y6Lc!>lVSn a;RR.^TaoeOZN%Ȓ׻[ī}A,Z}o|Lt҈_4bai(72.aN9@7rx.٨=J?io (/@*X >sm_ѵj"/X׼]p)ѐm}(W~޿GP8 z*ntGt<5Y*ݼ4yTedJ}- ,C&MOE-U@٢qjGSv jiA( d$ժ2~>25^o6VWSu3!ǵ>w#yܷDW>nFӴdcxӊDs|{Gb}3"s>ފն>fMzC .yN F79_$%~Z oQh!2evo}'2$8B[m/h<@R.A:0uRUoRKQk]sR=k#n%6CySXDb~蒓r\;d#3!=Vc;9E.΂Է(#MsI@SUX3diZJ'w;XgzVn1rrw1%"E: iO|Xp7'0_|2pլYƔ_j^(+]nѫ ٣ ~sgzLG7`f}έ q*5OCD}54V,>ƆЏ?%R@8Ͼdhl&)]ٌ()!?zٮ }~WiY0\|Qt%gibJ3>IUO #eilS>bڒ$ϚC#Hz<=l/MN0nH0zAd#IS c?PR $6l8~h}`'@g5e{ɶ3IQ`^'Mme imW0$ZiK{TuxE_ &,\k]wL7@9U&tX7)؝4]_N0S#5 f^7et'CCT#:A?RUOu"@f6|k>2͖[:8Hqh?D[kڶm۶m۶m۶m۶mw3w+DT裍Z`D}a:t=+㫜 Ȩn]~`}X*@iq7O~qJca18_Gg&']7T3OCTY| *`[:FIMꨱVz~?&rVn'!`ɪK-{g:IeTQSq._֑9?3p*H{ྞ* pq ibXiZNQE" g13HsܘR R2Mk켎Uuh`HM>ks \"UQv#ũUҗ+t#S1/ˋq%oş{5$n R&?Jm,XqL1Ԟ wS$v}Sto :RYěĆr4A`f%jŞq.nG9T-j},WT> stG#D !lv=þpbb%\THViWF-%pݍ|gԍ7 5()PGHKI  @@u#'  ljmsjÂl,$-Jr"*42pDQPn;萏t),iD5Wg@w.TG.&BWYYY,>*0`R8!";j@Vbyo?'[IkmqĬh KU^߃U=bbHB&/ꔿf"Um?Vˆ P`.Rv+$"ANӲ&A6B,>=Kٮ03cS*ft q@a/C^OkH/Wf2i"$m'-'e@DCJ;!FKмQn[7QKB/|Sz8׮ʸ̆xA! ? V=1gnn2T bT ^@@PÏX-)Y +'z(DÞѮ),\(9#o.- ecq`σg9HҩEE^тH(:U &@Ċc1q~ q(5o)N4==/2o]nBX6gI?l _mBG'aBKYOgZ7ZN5VKHBc79D{)D{a=-vB I2Exx̨h4{so^w!n|Tڇ5̰f>ⲻsuv7M y\]䢟r:̃ 4%5Q#e@Zkԩd62c>6\~+RORZ:] 4 b=~Ҽp_)i y 56Zjml]]sTi|;Mr>"~JCz︵A gœ%Þ)M`Zf6cf:J6ROC(. &␡Ζnƶ,q4\7.RB8$K ckk\gMЪl^snj0ʃk[¡$u&I88֭TN.ks1=%o+K%ImG(enmΩ6e,kz!D&nt=/;oSSFM!"q#qZM6*q8=_8'(9("Cɀ{Dx=nwkM UbBu'Gu- 0E(!(ȏ41,AbPp˶˚F5ZF[ү:&jQ91L ODiRdת;ziD23h qmA1E9يF s[x EYXtêfE)r)}on T8K2ܡu}.iA~%8PP1`_8I  ?Vb0-r8aК;[ZLZf'RH9_<$G3ҊF|!?S=7Ss:gÓUVl`=ҒF)sdk:bqC:vVbv[ȡ4s)3U^0VyDN֫M,o_"r0nro#R[9ޮOۤD5[pnse {e;G[Uke?xWd9M8 0ja욀 le$/ a^`E-t>T1$EN0F> DTÉjnrVujcMT^C=Yl͑db-x2ǹml =MqrR1z`jڙ逇}21~-aL`3 FߩG 3ݽ!( :a} ov}JLw6uDr{ j*K@>.xpm5~n9\nҮ8QV8 Y7#p"W l"ʹ:Qٱ儯U* 6~ {ʖy0]NHP*#3ltI?x;RR2h%+ЮS& +h!(pmԯQ,j&=>|Vɧx ;נ!B @!גä݊9Lp=n9W%P6(NFf疪]'A)]vFIgܿkWHFTĩ`I3 dԇsXּ>M5e~A73pdIT^3uJ--!K WUU `% L B'<4y^7 ߇-nLrpv馮86 oGlq9%O`sQoGLQ):WAƕsRعaI/8Sy郉Ԓ@!d^)C][L1,5LAh/-: m/[ij[eCjo {0d#xZ:CL>kJ",+Kj-PT%))JTҟEh9J P2O+2eT1Xyg >B{GGoG@(؃t=#P%sŅ涗txcec+ymӔSՅj_Z%@G*sAMёH3~pG6٢xI9K9o\8Y$LdhaB-ƪYgɕ)?;oF48NY`7f{TurRsAH`0ua&85'!{ȸz`Y|` ~~>`^{ x*I-A5o/`XN +cwv{o> &cDL{ Fg".=S#\3;q\H !! 4lz/zአ1r!WR ` FJ"껨nNˮomt>iĒ2B.WM57M]I(4ȠZ.)"cK5 ɩWqs^%~S#,X X&FqFǾKvt6=C9 ^.B%24w]%; wAq]vD!f3qWutO&23ߦtFlɭ6'[Ԡ073[Z IbA5>fA`guqg,gB?1bK"Oo!z+2YlN3Z(U ٩;9O15&&!l/Z܉7Ou<2yebGiݮBuT} ކMjde Y{9 )naG аQq$IQ;N/S"r$/(bMvmh7sI?,\JƸ@zqv%F#Vpۻ^ĒУ?&-AچCYg2[mkaj d腎7h5rz{f;ޓHx;Kr1%w%@rOF+H[UNcxAmIa3 2~gn Э萩j. [5!01Yo[>o6Nh?We#gD n Z58z;q.wb 42wV.͇;4rc݆p5v쿦G\C{B^:>sa%&7-4$dfnƜ4O6|. !EJD6ȢmѓP1lFVUm"Pdk4Ռn[ Gd*&ޕκ#6nJp]Qs!#>$ڦx1^<# z؄[99q73"0NlqmCCh^m$ 4䚻uX 5anisoAo: o;t3upyS#r`/SO$i+$3؀aOTC5n/>76*i'Q} g؅&7l Q(aijQkV0,4Okc<3)=lvbI4L=}0H5|QӕjΞMvگ#u5exH q^E=km17-^WhQ_]nޭ{O /ZT)#(ٛ1rh;Mൺ8Y06}.Vn؀{D v""Z<9]1I˻z=kw}^z=L|y[TFW wDc$kIw|F=dU7B2?ϱ V-+/uGW~u*(k0#oGi!"ba Ɓ(ɴϣ4o#扚MKV*9P$ԗDfbO<1oes09l73stuCwWCڷ& zx~vGI=|_INӒ[4:y.pAz4R[&W/ӑW/:of#;/{hZ˼ ~:$|wOꕳO\ِLEpumʲx&U6?^?elDAﰣ1 @&7&B g |BF Uꡫډ"oa͗5ǎ$o 2L2jUጺ9 f&ӇUm9mԝb+QlK|1!`$aKO uqDʁ[ ؏@Qz]~xR|{:' Npam8icvv2B6RrJ%0Kp@2yݪi(oy_rd[sL ;Z=༃F/]=pAt D&e!ic~}nҦmK !u9-K^{ld>282^9CWqX;ήcl bl-=K}z5_gTVfDKHH<~in%O~}b92K"ca}DؓM* q|w* :B=@8RhtBʸ'7Ů):QRYu9}}J~]BI^';. 2غ, mwmS#rM߅;9Jx;_ y;ހݦiy2郱oۆz 3 җ-ҁʶcv ֦f5Ob|J܊GHC QRe?騾~H/.JX2.*^ Ӕ%BE$I(D.^(b;YC ?г1:t3?8"c3Bn b6!U"\y| [wuV>tGD{4T{purl~m~'gdp$4x&md}(] Uw؂M:}Aˍ}5@W{`ۺ߿?6lOWDW?w_ީ3N>w41?*̬پKOըtLZ^?Z]ݳ];ј۰F緬e?tN2(?'#?tM_XEc'/̈?5Soyl', OIгѳ ~ Q\G|Ȭ.^$MLamTfsmER{79C V+e5ljC0Hlt-;'N3".jv.S^!k,2?@,lZ79NsY] VQ;cwumMоvToVr;OGʡz=u7 w{Y\@ 3Im)=j]\ߖ.c |=Cȵz;3>=zr|rWFFډR5rLcI?q#pn`K:[07,CX6t ;f e#FG86sZVCm75on;ңǴ|m#Fw4[u DvzgGL6{|˟(LKv_Je-E%#ȓ)HǠ_;OLƌ* G 92դ["+嵳HOgrH=2JWᬫPKPÖ!5CjChޕ#35q=#" F[6nUЫa[+"yݐͥr陁4dqJ~z xb5"O=fF֢7Sa JǕ A0؈GZNZ{𦼂tس˱4Iwa$|;uP/3/R/a/ܜ:H"0n6dž/fonJg#ЭL"іNBjX7o[.=2G~p|0yҤ>V4A,$-9&HE; n[2R;?n*͆,%Pn#- w`׺W6U)QzBQ:x\O{HcUxM5;x)y2ե-V C[Yfݴ=OrDpwuB 9euEKnܑ͍s-\:H/ep+s){V}rK˖18nQѡɒNS<.ZbFTd7jbX/} |G*hvYY4.i_}1n`MR_xõp#T}H_Z(u&.DF!i.Rit)_gB&n0q*%FE(722)G?k9 jIK)MǑJ,_lZGQ4 N{jig3ӣN VG?)jlz O7gt5Uz ohֿF|yl:zbnV0dXtB q'L|aah&<>@E2A>>] l>]h; e_ ZH Kd#;wԋeĶ P ZX]J nE:SRr2euWGggM؇۰q:^DwD 8O]Dן}%l;On`bWa $x.J1q*#-4Qr5KݵO͑,I$SYUz< N+ElSUW?)c#;C]4 J/j=unKMV-$'5/R5f2@Q,BA rfqaS|ȝEe7V tesX8هG4#D1y"Vt{yre mr ܳxC"* Q4=4i(9,Tv J %l [OۻshM9 砢j_ 6cvLL,חK֨]N-3LU"y'7\?VsO|;(s+|I8yrJ`# {B= paazJ98=> : 2:te K81ArC2͛ۜz]pI?[8$fN *{$XD?7zlGv;xʫʙ2)+)>8?!:jTH)}3#N,NI*%5% ӟ ʻWZwƤ<1#[iipώ!a]/2wX VVgvO Cs$,}nL©0V%2&R= D xU~3'Ro.|椯+>z455`1{3P6d`E0UE\)6Y}JYZbȠ^+t1lL2 k^9( jnhӡ"+&J)3o#7 cE"|[T1@N%7T9@4UhNqr⯩#9jVWMl0o-;l2T+A*zB:d㭯'mF-;A߼zP1S8^ϕ˹9"ܠ6d!/{Uʣoqh܉5L 18\pm&~F@٦Eg1߆LLp)<My*MK~"@ddN?-g!kBX' T yhAUTx7|y g=H#Y%j_y}겣PvbRde}A2iJ77^ϛde`JƠՙ]VBR^1ۏg ;Gr )]gVnƆ'9OkrM$r:M$+KʣJ[ww9{D[FDjmHZJ# fU#E,Ҳ f{vOKbgiZsyN 6%[[vn,bȓ75 I8C8V`f V̝u@X (;~Mx#&Jt5O2DKĀj-uy,2gK[We58(Ξ Hvk!viIkie7y@9Ù`t3X9o^ǓEȗ/[XO|KidHY"yd+X_pe3ȱ }t̀qg7=S(Exd>X(NNjm;i8j)7^JF2s+A/D$!*r2(Fe VT*)۫қ9iI{h~q$h$ILQB]h&c0'/1:J=̛<8c *I+I4 %0$I2>g5 RA!i.u')ю kRܨ8KAհs&o  (Ro V:mHfXРA&{ |0Z.Sbms;i0g5s:ɾgd]XWORS8+5T[[R_&TfEF*;tfIc\^ZmW6(>:֓"5mcP;7}5_G%鐁FA;};}W815R\=wy[=^H JM5 :^ { wqP^rwvΦ4Qة ן-eɁ(L&7{i,!u?&׶ ۩1z_Djmolma㨅=T>@d;r39g B1v*zsh=/';-*O`kg6m>xai8v3-q34 5P`#QSX!hRz()ܳ:&)Q i)Pdas`NZϘ0?s=csOĀvxJbQAtxVq[ (d:so"S}j{ejwXYB  k h]BޙmI,`8e*f?չu(  RA#a3>? mb40#ЏP]PO +rXGBFiЂnR|7I[/DRb\!#2$3mxbf*y\m d.AÑ^BrۑXD]=(w '^kj E)[4_p~hk+gr쁸wG:٥amȦU8m>Js#Ξe{8Fk* bt|p1%~6l,8$ȭ$ %'*FWU"gzc }Xۧ nl!L}ZB8٥嵲ӆ,xz)S8(O! JB=BLI+ر`$puj&p̠16.3;>OVC᤻]b)^KU:<\כ@#INڌu u j'Ҍٛ$NjzђJFyBT``eNKY |qHoU kBt%^<0GL4/#x1,*hlcנoN)>㞍_,w M1&*=ݑ/ɑBՐA2M %9kYAəR |#_HADܣ { fֳxݟ2Awݛ3\@R:QU7 (3kG^kPe`h=# ya\wxAyՏBdH⍆p^]]&ά|k]?z}j5ߎ\YDt4zݛV%9HӞgs|6t=57 ` !p_FU{`5(d (^j(ٍBw<,V55*K]EujXjKtO1@U >m: 3xJ~&/UPP3yߔBMO[{lf,/j5޷NPYl6DW( 2 9,yx8 z|w z?P=)Y5Ex2>͋}Hq؉#dxx0 $k33oԨle5r8%D#A5l2z =P#]76aD##SpNNE`t\\5GTdސ}^ۘh{[uQ&/F=V|+g<ԟ䊦gĭ?d#NZAx;:& 1d "gM0z}!"3Gvg{؟أXvUGx$smGnĘ,:$_ ]=]InKw0$ʂq SHxw?b{Go J$Sv6[ٚ q]8X)S|f]ESruQίjoOU7lV= \㴹%0*U5|"pj>\b0["lh/!𥒆9 U>PEm-b V_o!|%}$#zzݠby6TqD< uev):P.I@CQg IN9Wr4pG{vO>3%n{PDwDQ,o^sQV(roR<_<$ PY7s!Wotc\ bu1dN$HVMW4Ȗ}cݴV|O7ߤi eTJ^YL73V3 i mQcTPB%k.[  DtF-Ta'΋<} hW;f#KuNwNmKu$Y)t_!!+#u~ Ąu4?dJmr!.2BR5^kHR.V\^ܴXx!5Mܟפ1/ퟭoJt!G/h&PLEtu͋&{fV!#m׍ɛ-֋2WNlQ: 4n`>YUfi;\μxcM8W8P)ۈoOHD{0pNbi$j\H=Mԍ:d:Q$^j~M; uN~?ֱQZmuuAJZ QJw0}6|Ѽҭpeocj,3Kf$4D8b!7 2|]%5 0'ZL$-\5,W,4G 0HID Le $ 07M[/HvĤ m|s<”Һ}_/IjF:YgA_Z$ Ur gţQ(A v.S2cs3a0̏DT} v1u(T2xI2Ėyqܰp3}Av |={ܻzEAW8q Nئ\-: /H0^//=9IṘSI_\i1 ^jq=iꙢs #gv,*ԩ<u"I$)h8~j!-:Zܲ@GCRw#{!FLL3kwJ\*cNդ7o܈GUc%J۰2^JemvWl6de%2Jg7LV#OD\h[QLy#Zc`*4tu꧄v!7y@f5 nTaGL8jīeGS yyt9f{eBݎz iym ,q fΗhNS#Veib͕`*.U93Pchzr U[5~QEnBaKEc6i2>9qczcx|HSw;v<٦P'@hԹ '%:$$IEw*hGտl^~y.ilʕW}^?nMVckqWqckvM%i^?*X1k{NKg=r <<<2*g'>5=oEDmRJz15m+;=Vq~5zŗs{Hr7g5-xg-I~2s̉$-*V$1M Uز{y aDsM@VZS{=4 HWVGgP{/X[X}HΎȖ-T{@ȝE-ܚw}Q#ob/J&ya\Nz{ЩyDLwG =0qpa҆ lNf^NرBɀχA7ZRѠYþO>ݸNR8֜.3rIpIʽ_u/5CoC^CP(6jF~ҎxDVWRD|^- 59lg5 6U H>vt+>kh?]P)YOuVH+S$m\ABcvXǴ%&i`nhЍ?b߿ ׅ5Z3lҩ-,H)7'՜x;nGL\`K$6pv:F 2On%oHBgJsb@ kDT L6:Q~DZ5=eq#r'M LkD}8V=psKOXf# R}]5+mbPgTyfp! )1n>>|hI{;mE}X'%\g\ETo:K*Mn뿾8 \i7xj|#XY&N+L0^6yx^hvdXR|?ݝ)3:{uTJNxE6tW D li qM| @, /?=!;+ FGjRBv::Mpfd^l͢\k8K%(=4p툅)o'Ҩ~pr.o7}3$Њ#x"W[ Cm"Bm>SLɜXzR@$JM>>Y7XJ+q@\j;O}ovQfzWC32oeӫrH"krœo ]="c-5)Uhor9_W$Es^%p+ ? iNtT)H'9w8ShYPhEO}V A5VXf/!,~J2&p;#At|tcP"t։t>ɱP-9GpxqC.D@1D2ɂES=MR<;#%YiDBoBE35:_; XXi֛ t,n\絡75/jNeoEDAEɯnϗgbWz۝~xB߮rL=D;mXb'qxRPP/&^&?hkɬ,ýFa;;cg(&f>?9"xf|f ݄6v|o)|1Xb~$q-Z EIAxZ wJ+*,br˷M5fI;sOvN؁r=l~JB i?ݧÙo2*Nŭo$̔ %%OJ{c1RYUSt# s|.HebA-Nŋ5c0b,"œ" y~i?MzGMṋz 8G;yc̸|ڕ\&_r ]1?,Z-0_u\oXB}֟Z|] N"}dž02 x=ɂX~\~ ަvܿʤ^Bv*St?z <ױ}鯝x gAX])Кn|ڦv|''CX̓ [XG:9#>Q A<^ӌa F_ }Η1'}yu_וTya?4xB.y@s=p^uFp~بDImXhLztQwGm$)0qJb: :7<r>q=~x4x%5#8]yn/xjb@/+ʱK*v ryzlgmMѧ16P3=02fI6v֎#.{;bU0 fۋc5fҿG~W}߳f{\7d?>gӹ-Xtl<< IVa!XAvCa`^hhKuVЕPuy7 5JAOboFcN>@B+P~bI.k }0ޚEkOb]#{gFAa8- >L/ :- n0!Fb@ziyY]j=9NK>*4X_DfDž6{ u@e} lp1~u@wSB WhMG>5x55tJinI *3V ="onaBihY .(6[]3]:vMS6vq腓v« ?ieP%Ԇq?RpJnL%w芋{NsR]g &H ΂?DL$.gcl$EN0H/EeYd(%^EQŞb#v2d;D/;LP;v=%؃΋EQ=$e?}Q_ԌtlZ?ͧ_=VҶ6K65ÛHJLjG_7?ޚorM.5K=I8 e- g$$H0z!Ma5BR i *:h5ʒ$h2IKm[;t\66shxظ{rތ)mG/2Xjն}aӥSGFoՊSTC/H/=ͥ&|Ab̂iF:_nJȔY2w8:oT;q=dFඃZnݶ^^ȓݴyM~N=W. +7H,oղ'[{I׻'Z{IyoɬɅ݅^%Uoo^s~[;R_޽nmo.ճf]'υuׅ^oK.7[XS-VhĊWڎ=?_&M7s:AT?ӏ$G3h'Gr\tC\Mad/l L8ZO~fֳ2z ?9\qF&B@#ckΔ$oY/I-ctܮ_n$i}22Dؾu9mef4:>GJ=qD?_}_U: ɟ_vt} ğ><}WǿGz{|$!I7]e eeu=j-\h;6ru=R+6sj+_WɁ~Ɏ y^#a=ipeב VY8IdǟO0j^OTĚYbJLx9~lwk+k+j>2Nx݇7 & Ra ⼺pxV)3Lu<[jL{\z~ %=f#w=_ãor"%Rq}Xɡ"5,ZKy/8x'\2Z#~JM~lܒn5C臊dTiRk7pn8[ ^DNF xwo﹪5nM YX*c j<Ι8j7|u1y튪g5YcrSԼڑf!vsIp'E5%61͝B}80[L;bb;$N<Čmiȿ jjSw6N@IJK dU3KT<>u'sc"Nrt̥7&- JB3x8[!M@b^CyPL,6}^C\OBSҒ*vr(l3ꨯ B߷bqTYޒ#Zj9z8*i=b= YR{MѬ: è]ܹdc[{KI?qg8-q--nOpy8.Nj(U+fWJ%awUC?<.#"KgW4'BF賝];;7YhӇ\a;t2+ XJ>WA#VDTTW뚛щhM 9,u՗w5Jy^#|=:4ón5%Fm-Rf& /Tn׌oUĬ 4F6.ṫ]D 5\P>x9,c%L)ӅiFıdx>mko[-.B#CHR e/49I]>AǢ)~Ri]kH2){j*bU^*G95^)iDvTzɳ'a3Ob:Jϩ.zڃ|aP]a$pz{~v\86C2u_[n6e|M)ǘ./\\gkԨtHޏe4'p4~J5#" JC!H?A;sMJZRYDj^1MgH&UȑZΣ۱A[ѡq!0 Xwۑimln e)h0;KWD]y\0O|y/-=֝3i_/㗱M<}Ϯ0H(qҟ퓥>c}8#9?SD{@<8 Eɺs r'FT{eV֠6&Kj 2Jo_I{ڦpDpp=ئˮyp| '|Áv4LwrwOQQoa~Y72e AiJR --NͶ[ 6Q LyI%4 I0bYEewYsXoK. DA8Iwk JKUT#[uL>§A%0uRQTZA(bP^=$派{]^[{yqV&- TåAb;j=V+S]/ Wo`r򮑵Ճ۷ܛ KuVKd"Q+C;'xV;"o1)eGFcj| Iv8@hT#;"PAj+(8q;3riz>-TJUv2*1 zKnq_99~&gyYYx1w=|:鱌ЈO0${.d:-ƀ(eA;<8BAѿkWMI2 4nԲdBEe⺺d:$ 2a[ ^uoV(G!pG_E5?+cyTo=-%KWxTk06qtXi,-vҷVPlz /g=PR? o8PG#?5]QV !>oóK ^ODD6y*)ËGQ#z Q3=7Õiڝ6z<+k> ^㯚%Sx.DC;k^xEyc&'я `Z !v`-Qn 4!NtY4/F7W/ a6ȟeM%`-#zzy t \|le3 %pl`ϭ//0oH|` =f=mC?휴(Ube"%n)slFN *+G+EwO$%# Ɯ{k58)sBg3׭F[ 8ՑVr}𲆺Mn d@OԬ0h)E\:¨C'81dQ4҂1]a!a$e|K7qϕQ%gm&.f΂ qp$w6xυTI 쬂5fuXD65V<6m!#Wf.,*kb¸D+RXSK/BDFF|2LuQ%0}Cbe>${s >CBZPx[sd"~6h4fis& 'gY5[}26 6upМfaxV)<2_YXg;uXgF48+{ǩK܂3d{]HOP() ga篨 S6n,7f8:V&=g7ߛ̹?D%sZi:9Sx|3W)᧣̩Jh,f~Q'Q"–@ȏʗ򨰇_MB*?9 5{ 4z ?[Ch8Al}Qr&(!vŹ:[ϩ_=g8 vQ@h4MRfd Ù Lg; cĤS"Ccg6nASF0ւ~IB&B<eQ!' ʢC%Iҋᄒ-ߜ#/AAZZ6&nO8,y핫\.;njGX& *j}d*!xO)O;אjN>#Bf$\(r1M|oxdW`y@r 8VH7ݼC:Tj`jr_U[R5FdAm]=1ZvRM* |m>jC%{Re3&%21](* XMn/#S&Wj"Y>B;Ȟ ,x񢤑  oh{_&6z_g`-0z@AT*.CJ)LBd ;4'ĮA{NP4CЏ N$Ap`%ŗJo=>66"ƨE#((xdD86nSn!-p>vJOx֛2*ts:blƳ?8+M[<ƅjo&W壼?1L4j( ?v',.S67 r6EJA+fLNaECJjHTA!(:cuv{c!Z")&& ,,C& 쐌n eYn`d"\;)i8 ˋJw7f[ I~66 ,u^Dml+df|^/[a+/r{>-K8J3~1|tI+{U]7,A$EϘ ZҦL9)0KJC ;-Х# 0zi 0`TWg,E*U?\0t*C-TAKB1-S%{~ Ԁ]^^,*Cz):1*kC`M{5) ]A$ "и9q)<%?n%kՒ+UŠ P%WƖeUԶkƠKXkRYK>}[<< (mE#0Ivf= (j֥ϕsIZFx+jңCǕ&}{0<A+q˞5̓>R9m2w˦aO-"qMD)1$5繀@ؿQH{ 85o./ϑ鸞Y6}}#DBljvۺwL^[~FlR؎_|a@a/ ԑ.eJ,9 x|zpvNΫN0f0M[/\1hnzb,Yi!ZnT33".0qN6tN\0e %Y"aeyut'rsو9+fˉ`2T!m'J|ܨ];|Zbzًϙ]̈r*ˠ"=صVidESU.\O1C Q($c|]N`G70>ކ$$vfjvQ? 4y6!ӹ1A64D@FMcA.ej!Xac|LfW mt.|MЙwt{g^v]˯[hi`ZV?;b'L}FVhU*)+*%HC%gl@x42" % WN?֘{Bz ͹yzpi"f`tx2UB֠x0Mhl~ݹ0lZl/J_{]ӬHLo1k|?KKk(ۢzwolqisQьhF\fCkDU}Kw_٪ tK[ ְï'`t@D[Xn_tޯ4F}A7ˉ~=`, UaD'<'cNtq|1\-|C1PfSNGRzÐр72 |"̢;&%",I/lz]^Krj?_oI~6okrk9t59‚)XKpҎ~pR'IwJjLR2F>fII!ڂL IW¸2H' b 8HryRёraCdH9$ )4QF9T$^a3P Δ'*Sɪ9&&pb˜ l[GjOvJu5kL1bTBELLzH &'Rt4EtEdUF.&#+S]FűyR\wUɃ k)x+̪/^T7Ku^NܘfLi(f:~ߧ[l#?<W*A*}m=ty.nQ]om{ߞK0mU&VK*VVqI,ޭ,A.->? 4^: .QPDyhFҚwBSNT5 ^\6)[[T)`,8BDre8i( rzl~72^n)m{|Tl<+H*Z^8v@ kGIy(<ǩ47!87Sl 8\I7xY~j}bm;nçjzd#\\&VbCS#*ٵZcj pC1x{ HkʝM087ϱ?{珎{ mO}?GD`SrЋds ǥ3 A+ 2Xd Lq/ ǹO 88\=g{lZC89YS'qT#[; 4qKX$ |$RQX83>|?m9i7[ 5f+Ur(3" u/)<kCR(&w;{( mOsȧk%dمՆƾ7!> cdL \Rbd3a9L˞|]}Zmx GVF =YaibT o-m=3JB$QNj1+ Ԝ#YvՏCNȧًC]7}+Xm4ٞtRQ*㠗f'td;,+4L#F#(ή&8;)yhD#ɻj6mņ957EI] X1hq7 ̧ ]ȩv[qLXOsc;=aS4$B*L(O&#El^h;ulJvm[Bys&q~F\lU>_ k4U~wIm9] Jr ']R9{xIEF{~FVl+e_ᢣw0:W]@0ZYyKWKyǽͻPxքqXzí8J~'[PrI\儂o@>顀oN%d>2]`*7$LlV)CƐ (d4$N3ٱ:aƶ =lP$$Ig <} LՌd~y]X(9VHӰh7f|͔ӬSĒ $n3 A&dX8Dq6{E2}$#qy,\W?a]*wkHQ{DϺ)ȣ=.ݶsdErnd~CVVXUF%\1TC!H57Fؠȥ jT@C1<*ɐ ! C )SX13*xRYH]Oiey([R X*,Im ";w29HCU Ȉ`nۅDG2wϞ!-kƚ"lc$9pcS6gJc8қcS#mSVIr`1  5:+anlA1fW_{t;:-nA83Ҫ4Ļ#\qI\\\=,yfFǟXجtRICE^ I4SYځд$U=l)z:8bRYs k}9w'ESxx^_TZDm"IE,E#wmƦk`fcy5w(([*-/(l+ <>ҝ9r ֳAL\[@EDL`o0g']  )t4kfKh[iT=3ۼOp?Eoz8Rw9l񂃮"gTЩ( ݸEx#1]Ƭ|x-ռLdܥ&Ys2H8qX89r%%/=wx#GA7Q~?_4&?h~ь,2#A£o9/dg_V1*clGvu=p ļwVCrE)\O]PHsu-L_ϴDBS{x}>-܌nݞ>6kNhXlyuzpLpom4uiTC.N'e6S!( d3H|ydϡ;wjTKWB[Ҹ¥>xޕ+yTgdWۅ3 7_rN2tHA_!id꒏-)۷|˻o蠻nDi2:}G~Naƭ+fx8m>WH"T|a}[N %v\WQv1۽ɣYk_R|S+~-^wF?'\_/_VDH+l^W鲱X{dGk0qz<0mK':<>di5RT!jG)"|*%8KfBU|%'vM} F;lB}lxل~4 L~ 6Lwͯ,an2FaŇjFj~\{nS(K?A}}m??6b9>**>5o~wHL1)f{ssN5?GEt;ekFMFZ`y~ygV? Ȗt@2"\a)ErpjoST'r)J$mU}&ZfTw=JaH(5cVjXZϐ E]T5TFS갽.=4tFnP KXcg9R{( b<+Le9JCd4c fu-P 8Btt-$TDE籵kgtkW0ny+/g V9˾j! kۈ4+ ۤ08kReBv+'L TA+ 7 ۗWON#묶Ҽ4P&/'<,[h>2&c߹W  sq 3:/[ xF7`J ~..!%HlA;ak ķjh/کfuPP<}B6*Gt[XWJKS —`_ G qb+h'=hњmX,nF^ 0T74DR_A~< TP\F5LEXOM8%lv@UQ42@g"9>Ƙɾن1Yg0l8b ,80L0SjbO$\ܰ ~/}*t^O#ʿ@ ɁW_|!JbiIfCɷvБGcձkXZ 9u #WojCKO%^7q%]2X},k Wpu+n"Z8wpV2äDEC Z0`?EK/k+ m#=b$w/g@Cflbbr}E@|}yuy_I}guw_m>Z}|ON 6xCCxxǂON &kC all^h[A@a碓EѝXAETpNa&zLCz$M*[{q>JʒAH4$$;?Gc.͇NKcw!w !gռk[@)r/Ub+񗅑j5Tcq>1󂞡@&6&fb2_[.{$0gv!z,ق%G|ײ\-FTx>P =T5c@IL1/Zh||[ vP81B3jB1He5|B޸GҨCBA&`K,%F&LG%  7MjR.O?0M5y]߅Gۤ1-Ud'yo9+y/ )FN2@YIJO Fq? ?C.XMN3 IFA=9J_2D/Ҁg(?-hF\qZ[D¦P"ۧXJO::=H GN3>[nnmO0ۧRxWn{2 'Jm$#:L7MGuW=@վ\Q46'w1mq#i{V0#KIES(ɞۉ# w3Zz}T[-s4!"{kk֢QUkfuٳ2#N-d5#ːU wܔ5k*Cb$^Ao5-(쑋t9z-DFn LB+ې{t˺t *hx LNF*Z:  I>sSL O*e˦wB_B 5TbVB6vG7%UU`<%UeSvAo‘dUڭhZ m5 n##Nzد3u8fd(tG݆ FBLQ{w 6؃q+$Π dfƫnZbs !WXG:>DOGEt֭4~ye 90 } E:P|al`g EjFe; S|0)gЅS<3H-<)RwݣO|ȱY# 3Ik˭#^QAsC\dž,=ErlxWNy!2GQ.¨|qgB8wC}q6WՠB8FoɀE3ڞSKrJXk:'uca+zƸ5esU'gpYCZxY\J~/^f2mvƛ&Do^zfyщY4*UitM3|`Y>?Ihc$J ȯ"'7+u`1{*Yiܹz5:zm`A(;8j۔*yOuvY6BLvt'fR_A?&]CcDy]s.͛:|Zz[Yf EWPn0YozKSHNh5MT?M>8]zRRlSxߙOl6/Vq ڔ AGօ;fk6VY-5ǩ}sE#i1EVIR#낐OՍ;o蛁tCZs~?K.cB-IѺ~8T&Q;˱nLuiEPd׳UrF@$/Fqo%ǽ#e<u9uzѽY|ߕ_ޅ@UhvC${ЀSxB+P1gҎ]Xiy/: ]4:;^qVgIT!juGm;-[KRSMsT 6,̫ V >r΀pc~MF[tqx=jx;L:rQ BWpv hci>Mkmc Q)jt sh;W"PbީցAK3qKJ`_P->ko{wv  =UZbqwK ފca5\fѓsd$z^1k}hM)˩+iko͓^+u+o Sp]5؈{t=l">CLtᐣVcD^sלe6\>3)4,%7_vh8=,/МO eq:(}[5X 3ٱG++P2aFR8g,F6thYQ11/pe,ؕϦ1[P +q/aNAlޟ#u(/*Ԧ[L/OQ>ROjyV͑zzlq gt﷯UɌ &<[ 0E;h Q^hL3gjld |ovNS.}7anৡ'WpG" lx4_3fF6<Y i !'-"i42?!Ғgbx@f @M&Hܟ0Fa tD&/o&Qnlc:`?(? |{ZL'#սSyaZ&#vl2?J6D;ub=z }!igzoSGzww t¿XWL7uMh>rH@|~B|:|SRRƬ.kJV>j1@>">zS'Z嗑~3=KFy|P?7L,1IZ'M{t6z^?Ҿu^Fw 5CwH\Ѝ:t7~E98ȿ8'=sN<OQI[^3"Wcφ?%6k^cטX ؙX3῍YYY/Zolg/?6661?r?-Z?8Y{L.o ˎbW#ˍr$,76'3#;'K_ywml:9)I,ؔc4~[џD&\$Q@^BBT PZeAHԊfn]ǫJ+Dٝ˫W˫YMϫFR?eMF0DO NƲ8;zCۆMo PIi MN`|ߢGM^7 )Bcg?;9m߭Oyjm+rtSL>BnϴTyJ6Z_ݽxd{]vy* _{ G @d8uυM#M܉9mcYSӓ3SoP&B]hX\!%klU:a]zqǏkk@vV%-dUX-PW`t>"=su(WWh2/?j,ӌ+#܉L?sFAwY4j+(_phy~ ~>c7 +_XFqtN+!K0'Vuo7 )yѹ';?:* ~2dOwM>c)fUqH~\f£tv>lCF$Y>QRC;#d:giI tYteiU*[&J"Oh PVU~J5DИ%Sp jwh#gHVWp1gKōwLI[Bq_% xn`LSv`-_L ڠv+v]AfIPK+Kߑ^2ih -b~BhOaT3lM Gl\RPL`/ǂZifP'P`jIYVCG,,-iHo0Z!(1k2.+هe'L)2ܗF-lS]}iqٽ]ҕڈ).N%78zx$dPOKs:[GkTTFЪƓVj_.S6{\" &NlNrs. eĎ׼ړV4's)%®uǴɊۇ& O}zl**t9פp VM iE|Y,x~/Wy!qm#LUMv = Hhѯ{ L+K>]HdWyUA|iw ?VˎfaV)LѬ֑xܵU6ugKozL dIujTSAYPMǪӗVeWbKS)sSxk LGl.btzXK5](4}ykiXbF*4qut'4ѯ][cqEa"[c.9uڌ3FԚ/ջcG|k&@Cu;M#AQx(X<hxj<-pi2根FftR׮krdݝ9WX5{qtF̔I ՗hF\eR55۴$٭s:mmsnuqu ?vxn ℀c5/*m@;) mpD8+TeC;itbϝnq>ry&p%&Q!{)  'F!Ez qҭaϝa8g98֫Wc$xAUUöBw{ɖlFqZ =HPLi)nؽ%"sv^ڍ99g ӓMd͛WLfs27:uZcg/(Vlv>^}28AW0 _nUk5AsZVXdUf +JH9{xx+Ʌ "yG3"B2?v*T.Lo6_BB(ﶔ}7^C\O> wmAf3VI3B?%ߴ:Ujk~́z!3#V*xO,NØU[0a=mm/JlSMEF0k'(*7Ϧ SO1qFe6nXZ]۾_2HAuew/g'" ݽ:όn '߿8l富ӎ0'{WF:.P`*lUBCwU#DZɰ"`Dp~]>ny c}BHƲeW~v?'­l_G(*V}$G2N~>ƴi9>F|ceIlq-C{*AxU22ӕqV!LHF&@J|X>OԯaEJ  +'*YT&>d%ЀWQ&Ii*&/Y+ٓFtGh!ɑδw4ˉFǔ+,e*UmpJ{%c3; Z QXo gNJW2FJ ͦ ,8J(OIC!0-<*QN [F BS2wArƌЫ4k`hiLQ^DQ`6η $Mƺ *?tFRnm|MPlo6㭭Hn뽽6óhi9 ȬT:|XC:;e勎:χCUn_-VW|'.%e1 ld'3[tG}KV,$ $@*iN6kVItKM "I}٨1әɨɤ̪rC}|KWy-^Y $m BEڄְ{l?4͒5;#Yw5Hf9XLKbF<8HF9s4E2Ja-IҜ4G/=^@L1jsyK*dp8$:mVUQT G#Wtp*[Ψ5h9ktrqti޹a0qOS]XsMLQysRBMy *1~a jq`ʑJY#U7Y7^!R{D FFԸf_Rc #«ī W̼ K\䰋\SsGfѧgUStVsV2̼{׬12I̪|Y:V=AќgɥA.LJpˏ~;fW_t{,ц%Qٕg齫\ri9 gmM啺Kf˔Ϻi|Hiv̱M"?NzϘ]RT+`Ӌo+y+>2m $})<Ϭ\gS@˼d ҈ѷY׬_XNOeK?o>!gPuIK\$a3n Z$ /Zٶ{bt2HI5_MS 5_-mk| Y x!Kkii<٧Ja<.y!/{^2^Oi3qm (L2cٸ8;E([?߼0)s hmN1g< 60uz4_[QũwYnKDNiX HLI1|v<ܼRF5o. tՙ9(Bt$ɪ,)/JYpڄh#産 ~"2VuEHkIHژF }"=ǔJ]zrlVefVQGKx:ɍ]SۖE u f aY5;YdM]` Qfp\p/2kn'ӧH)Q" m/&c0Ȉ]uϝ9SfWI=Q>ո 4c<"ް℥TI[qջڹջ:?֊{>ed+ OeS&01 NXЈ8-TA^请'|$euKUlvUTze3`n3qdd339jĪ}Zp 'IEzA"~F x#WI&WG| O#njNH#sAVEUA'e9WpP&Gq <wamT1gSg'vg620abUD- EF Pn@q.o~>NhSUFYX^9v}mbR<0#1lA %'EŽ@,dg)rBI(OπV#BPA7Ca?tPG<`mbXG$ɵOtPi܎vyt!E00?HYOOL_D>A.9&m*HBR|syi3{?:DU#4s~N6r2MPȿAbfkdes'ߌgYD X'@B"xhuUË3^$qpgRIvT%Z`:{0S%;o'>uup#m':Ͼ"|?O,Zx~)},£8m{0/X%Sy/ϲ0eN{{L+}]XY}nK?wy&f\7k5 LǸY?!- gPT_bɷi1H;[a{Ѕ1y bxݬϸ\jmA*lԮQ1}5d%_Btc!A4:l(GmrvmL^KCɅ:>Zz o39VCO$| G;iâـ_r+bc500Pu1 ڵt #O7@lآ\Ylu>!4vyf&rbr, 1%V9f@JX,FF~OFu'jh XN,@p:9Bsjۈlnng[d^ٖp"W݁7@#rH"`t1:xvXuwg.7w=#BNJ.{1r2ASd"^U¿i*eְvا)_Cn&{`Oߓ.X9JӤ#S|";M)i/8]>ڤNm*Nf>Դ2!*ڔ|._IԘg%W 2Yx ! qdRqJ 4|v|q)p^kCRU6t]xow"X0}=6P} LHf|I7u h0:^&.c8%7ByMa|̹WnjS6(@FٯiD`VlLp]@LĠfuߺ5SAX'xudE+?.ݪsV}>P]}^G)WZ5p$*EXp;ꖒzI,JUDLxL =gV?^,? hPjt1ohV5=Ȁ\2NJ4@'dġ}q%~QFѢ^b][m[t1W*0' BؓŴIPj4ŒQW|\_~W}H 8783Y: ԗ'&Wv>=vA~}|b:}e-e/ Ռ\ so2FUݓJw*q™M5Cve%2 !7 ĚeUeoGUC5׷W9 #M'^b5GX;\rrtޘ-.Lq-swÕ=UqS"Dg`0+FtCUI >8tzײ7k+6+o};F?sՒjnc'U_MhS!(^"2NZ'g{! CЖBƙ3aw>fhCLDVO3֦ʰ[X9TKC;O`YzNC[h cM$EC# :Fg!V#Ac/=H skuÁfbóEiZj$ꑮ^3yqQ N _]Da|bk$ Uͺ/ FM!*bk fffП^^z4(2ֶ۶"v|}>:1 Vwr-W pa&]r^$̖e(I+&΍ /ie&.coɭL p6cCP1 n6Z4Ғ| b7y!ê ׳ޙ@L&jaCK4TSfUZ YyGDf(n[#HMؚ >Rd40L:tju5F3;y3g9ag:7 d~b*y?_}oW҅m)Eۧa=TB@,GrKgPw X^/pfOu;0:Iՙ(Z+B2zh0\23T SJ CCZhC#ICMm\6/H  Ah%>jj"Pʢ7 KưlqU q\ (+rún`d ] J -+1*>LbXZ#M7hEz<CF+?)Y]L1'm)(iVɅǍ:<\P11t0Ή V)$QQ(fl35QlkׇfWwiV~)j@Vp`p$G$V =kYIk~qbbed!E$(g–3%eisZe%:C0 6CWW0>FdbA|D7{-jɎ UiO\z$!jO6k/^TL :SJS# ~#3_>BJs âYcM/Ru WP}T?S,?ǽ7.N3p*f$mj(ۖb4a]! NI`7i9Ūmkr3&xL7U+~osb?:fck!S!ː WGpxifp""GuWQ~;eN θÁY҃0i@@+vgAQjy}c5(~HIdfr IJd0Č0Fex~ӱ\2Z#sC+Ok]&E'^‹n|)2dyZ:sZZ\PE.t)TAOQgj08@Po,@iiĿWnׇsc;RtIRŸ~%Z0xqطRDΥghyU(wAhhl I O39)r%X.&1UTn잭yOyO7{.CzdN;?+6ݿ[|'.si˜/AP/izb|72t2/3/t~(t(Ţ]djW5 C?9H)=01 ;22՟틛C,$tHt',t{æݷɱ 9p3g%.1T4g8nnqqj/,ř`ݒ!ң,Rvx6ށm$U۟LȷhqUtqTg(M*X:scPq*sۈlqO7r$;qh#sql9lЊӁJ$W!d: ْ0S3jl'`FK`׌lAMC\Wt :([<*.YơМcuvrѺ uV<9+]~kmuvn Qn1ތRqV_7x)y@L1yHDA/Ż,Da'Ps yVDSɣb5M3b::kRBmN YM;>E{@TE:4r|E\&عLj[-q޷#܋€OﶭyzzJyg=wĵKM&QӶ%'*6kkڌ0ה`hdX> oC%n[~sjjFQ۹2|njpy~}@76~U\mT$;yLYAP<=:q^W;lw@}emո;\5ivIQqR4顯C+4FSv'6v9b\XaμBtj=hiLsۻ~Mvl:L]}5VҮ` YI5'a4aN_jcnR ܵ_Mϱn{:ztkpQq*c<jߒ .t3>zyK(~,aõ ͅ:gҠlVZW۲nk'S_ZZZ|bwy~ny(N{cE52 qrK)4IU )  } sJ r./3rQssa䕅-2Dd j>>}܉@)XύT@{) +9-_-AbHNZӓL*-?JD_' =sMw;s!u, oTkR`( '7cT*OA W H^avZ-8Hp9n`ܝF"J*3/Y/͏J֤[[>K.LT\+eXB锊DrB^.@$ñ/Oy B*1p;HMjvPTb!9q?}/<%= (B*u{ '_Mʻ5Z7o?6c'f7HO!O?.@"A@zTf5yR9'V-Q$N+Qܵ]YAsUXQ @PAUA}NW4VLeg7*9:>zA9b݌/O1>ɿ+=*ވ6-*}o(r0TXOܒzvu[w,t$M9nY#) mz` KUKz<@Ԁi ;C<* BkivYgrOJ`Saa$D !֍愮[ుn9qВ֒ 2jgf|guYrz>?gk: c_u^x!-_)X%ӠQb3n-!m(mw`HhYr^rVƂ0{C]a\ øniQ 3Yt3;Rs K3ݻ)HXۚ\"+{2sY"<qU69sl4[@nz,c3y[V=j<+!όnn3%ī`vz'SqCvF2>o9)أG'%{4yś؟Tx\/1l"(C_X0F0j%+lllF?z͑:j>ǐ ::}LJ`-[wfI l]M?׻%<\ܯ1x9&g7 ~bLf7=VE?}ÃvZ奒> sSAsn \8CUҥgdS2?Tmx_Ѝ9;N/mΛ"8Kޭ &n#48M 7W/ ގ^USpR6dv71%(+t ѩr ]Z?dtltGHnq9#r[o{ ̾*WiܥӞkurQ3{sK*&gIzi>W(ۚo9ng؄hLe:+ۼ֗KQ@=l؉HV&{16dҡ)dҰh|$,5rz|F%ja=(M:ڐAh,~>rk>枌oVDŽT#`ܗ~]y|_E$_Y \kclr"K\ 0AsxKo>ޣ8AFeM!ƣg8Jq1^䦸% sވTT{6H*R<4FZv[p}v~6?vCފA]?`YQǤh^&} 0cGҌ4;<Wu,ZgP0AcdHk`m;]-X Z]i?^t^#v.J^B)  U4^Кj(!:s6w6=ݺR`_s v53&7n"hjxV~\ Ay'kC$[D*v1H53|u-^וYHWFq,ǵYh0 NcgpS&GS `33N `^M*GNQ3gIȀ&;L&LޘٔA& \a&L޵7HрzLRc:b8k'T7zsj9R] )A,filyY{E0~Of(LE361y0=`b;.׷m(3q*9I9y)K32lル.V&4rW7Nо1pX4hD 9'LvᮢL4b=6FmȫnLB2JJGyFmgz;BSs󴚻h\c~?vvލj-6j<ET,@E͂<Ӕ<qq]b]뼢 pbF;`i&1'1 ~-q=dZIK" UZ}LG|893y*8BzjOOMU t\q|<8,p'^4ȔI߮^`.q9)⻫8mc9[KFh׆V8P-Wp8cGq|k`ιt\Y4bJzgCA  m 3pX0QDzDCf-Ϗi<ݞ.=osDR2DECTk:ۈpNȩG!+c{n/[羟J;n~_;n- \G \t0VAnݺT\^xoWmL..w|ːzb0b[ njшrሯX@/b,BI(V:5brįsKcX "UX #9=+lX^1ҍ1ِbܛkqV~D]y& 1Bx(/I6\ut4Py\<ତ 80 r_b)הu -9Exp'~H;4Z e5䆭 ͞[op/;0W"cޏccWfS-AOc>ǒͲ[ [ 6km_`?k??nӼ\o $:-+)oaEJZ^ˢ #(rS Zbx#C/t:Nf!ݕĐi$;LBG:c4--cHF)9d:Mt: ^WI.4;Oөۙ/R ^?=< Cf(Sj&[8T|( jScQJZtɀka.m HDd.ncJt%wߩ.,)7fo $`l:5v%"K^9 K' U ;@t0w0?@By#>.x( %5' $!Dw.FG@Mp@)Zr$V OrY!Hҩ6&)A`IHu"Av_GpV2G)K؄)/|BDXTgH.f x'ڌ,A_u੺쉗*͟J!<$rIYu/1L@-IȤ8d`#!:44A&4З"yRk b- @F@hfP調 !9B zSݾ@2^SS&߾EƓtڸB;jV.Ѳ^?]g4"?|N͘BU}$uJ0J lh;3>9cm4dYvv m0܄]o \d0Nj~ՠ8RC JɺnkAh@B8z ^pcT<>lÀ7y)C(ܨaء-'',jPX*ሜa}>Bm74!}~~^`6v.3m!d9Ln,cnUi#7̑ӄ܀,i2Tj8:g !_J0B$<n0}كGI[R^ik]qW5],t5<o'wv|b3*^3i,H8 ?iư~xk5A _i7;##{h]䅬\'j" 7tyq`+&l  ԛmLscR`t5aׂfvϋ5g>?I/61*<5L?O=F_Ayۙz٢bo6}Q OyZ׃(ejጺvOKx_E)WDKu H!t+2QHL5eMkᗐuex o_\qóW7%Υ ^23пR%VOq {Tٚsn\[ez#je,zS][T H#$3SXLګ'rysan O7NHh"p=m¾o4^ek mx&0ϧJ87*CJƒ_“&c~GtXQO%hjh|EM7f@cN`)50TLx~v|Nx>f<ǯ'.~H\~Ι+jYA5Sv7wc|ɱʶR`UE^3 [Xwrnճ? Z.k84ovb(cct#=8^}<+v^Vr+t+oy4RKTB>Ny]aNd2xϣ2XΣp/zZ?z~J zp-&&R#^T!eh!bXaY]Dd׫҈$q$ˌLӑj5`ڮvA8/>pq_'68x [m>xӣ&>Io-PtlL3jxO\LKPWscY}}rhi~w&8ɵΟ;`dzz@홹DVNĂ3fN?O +2zP]oK)eP?Վرdv,6Nz+=ކ4=g+syff<Ñ|7t L@/M+ydؼdWƟ`ꥺWgjwFMiezdЊ3p}tq#\$XRM's|?9̱(qM"+E 'j#q&4z#-CM2IS9cŖ`gpFi5ޕa.۔{YFid%3H3 wl.3q!CF9sȁKb^XjK0Mz<<ĈP㧌8!t9kܘM]5>Y(a=)c1_Tdw^w**hu弶ŝZ;GBLo3Og-9dp[91}e C_]}}ig%" w\^-O޴%_tbkoM} =Ke,:KW߾;֢w%С6E6,з÷WWwԞt &CD'7˱۹?3Lũ)ִw779X4'Ńզ_$v͈h"G֏l7Gƙ)&aZeMKnѱo/lL^qej]WwZe(R(n%-ݟ .+&YﱳOzEgc7Qhm]0QPOiɹfYn `w4[01j|֪{޵Vu*b:@Rhñ,CoÝv¹);~NMo?Jkh]%W_˨R]s*r{ ߖ8f\WSs[v{]ݬ2G5䗷qY\"\Q$O[ ksEcx|b #U㚣E@e? VF &d\f0&B_TdLhϒ~b/%_DZW'ϺCl5m-LCkzY]ōŽ}}Śڭ!G7Y6";دq×.f># |yE?d/[0@×Fؐq"`dsR{͒RALewնԣt ?mY`Bgy]\(rƮlгԉ*s?<+׮ȓ \-GF|Q"AmA귒 e-|R,4!ua?|0m'z0#5 7yooƍě ЛͿI{3r{?MIMIM_D7! ] 7&3PSq9GGZNOq"! _|"?v?L';ڎ8%pVbEsĦd;iS_>Wܰ~Ny|Y,f9-Zᯯ'eWbK(߇K Fo7|Ɉ3j-"qz 3-,]|yљPycs죮 }KO/Xy׸3ѷ嘒ӵ}r=˘ GXżG7e̾u?^a!`□m#XZ':;"_G<,~nZVt^[؏Ŝa>'2?_z%yc_MPҁh"kExs! ǚpXlA_.WLO|G .ɔcuWm\> oI=]1Jݻ6!f{!%C{*,VW]g̽!ww3W_V5<GZ=FÞ%O{+vk]U2d]v1/zgs~<|b/wjC2j,ZqbѡS\-:*|d.}@?0n)9~ .*Էgm cXZ]gG6:-I{X]R~L_Uq9޹iuv9>ǻNy66W5hv-]1`1ըIb"?ާou{C~֔uƵQқ^_;c\RE,{3m ]>><*k^*aYg隿ogδM]iW9gN-H+-\TVg# +:ݵNL[)p@5+jTK`;a=P6Y1A3 P4=y@f8_-_Y{K~x??Ņ0t7WH=x\2&laL Siۈwb?+ԅ{}+rmkݾQ<[?談#ma6ˇm#Bw,$ VxˍUr<|kֵW i>[m78޶['ZQs7^$xPEa{R!V{~*"ydB޺{s;D zb`dƍolUeox+-q.-9 >&^PJ.ӈF}骴}-OVl׃$ܨW9&v(Mﳦw /rmn`q/db;e }<$Bw,mXf3S.!}qv/!ڮoKXx~U>b"JncY5;iѦ;t-96\QNLܶójG}GΓ571^F!{i &AKl7^kYxCCx5DN_o_|gK=p%Capa9/]ylH Ik%G]컔,fyme^[ IWyOVn6#?o1IZkŠΫձ7Vߪx";j SRi,Ϛ5ܶ F˓Imj/%~A{6rE|+c*2Li6Z/?$X^ v|u<Yfx)a5FTH4zgX{lؠdRe(`瘡w '͟%/pHZZT1a(Y@G?;+"D SɖWf7^Nb 3&Έ2{d9gVx}pYbOr=4U]3bR>DD+c,ν%1F:"XWt% T] C5w `P$ID ǹ0ikD^$G?5.6T 4F |ۭP;_]ጠU޶U*:Ck|._f}a1o/'sTV):?LA1ACuej n^(y#$&Z^lSe}ޏ%4ᣤI2Oo:8 Nͪ Wl=n'CGJR":՟45*v1&@7VpɷEy]sͭH~,` dʒ xm{G3غ_v$y>?3#WiѰ^/=u@jc\JӲx}v߳|}];XlHzZROڱWJM)u \xNy~-win.g%뚪mFn$W2nj-,٨`O֦yY[ՎqW[cY: f1-}o*1Y[&_ngKfG0zvg,+U5V9dxI4lݠɊh+h>7Lj&?Y0( C.b6]] Nº,\f>kn<5?ySbH1PxOӼM% ̯U|);*Uf״M>l7BFnP\{+'{$gCfE-/^Ʉ~dd?T0EH,aN .}.sNgk$_XqMZ)6C>tdK=S^5~Wf9dsQ› ܌7m׎Z(y6V3 ք&3 m4;Weu¾)>+7Ԛ _`wWg"W45w{ˎ4kN=eU.Bo= sst,ܾXhe\e^ՇVOf^Mp} |XAa;dֺ?gf`[tSDv^2v9uvoo2ps&m!?䳵QQ3Nli7K,>S1|i%ag#Z\c>_9zoE/ZD6~Tk4(2t*g ie]P)D^\kn!`fǯ|gKfb67n}S~Wxo.#ӱ LO3vYm nKEׇ7:mqY -~xoIgFOuvQlģk-xvvke݆}3%\7d4ǘ}o~QΨDDj,bֺuubs}}E[%v"K\R3gnX7\Om^,,{HM$ԅkW~ٞ%O"hqqVy bOܼ~-#w~lߣ8{|iޏe|yݗwGyvZ63 +צՊNnbN37X=f/?lKھ\8$ϸ}Ļ4=,S>}Q<VKY[rdDӧFՂ]V)/Q|Ty\`zU :wj@4VO8]V?WN`I<ᅰ~hзF^;--txC,;9Gf)\~Uަ+2c: o.dk>lYV]d v:_tUGWT;w՚h8)9]yHe})(8-fx鰐m^"-,:sWpsn`w9:YdGurƢ:Fnz5\jO7j5|MXx er`W]>rROnd ]0BD)q@-Mo=r4 \awayQy.} T8;;Y"ثL굛yjwOPys2Vsں\ݷcaڶ P}~+δ> YfTyvإ|x7jTݻCY;cB䳮Jz;ص K)V6 *h;&u5\Hαs߻w Ӹ y.CtUYg{R߽y&sܚ&.>`r_";*vzf.f +/VI (܋ 'l-!=b{lֵCRDwݯq\ڮw$1rpBv/OXq,X۷5ZywΪ+{l%b˝-I97`EW~Lۙ"V T>}[pXpdAw{) b#3o)ŸeW|bpia"{tFNlZ~9zF0l۽La?5iݻBɅZeQ\GX{: -"][-$o&Zѻ?I @DМҕpY6 o6d8p?g޳ sW4~u7NNůS+"$Hz~;{h=rj)98iob>c-1z j0\;⊈u;[9NMc/ #ni^0U~y˗Vil}TkYN,G6(*/;&-rZ]B_5H(<=_ژ+~Ite劋+_)bb _+e/;sQ:[{8tRܫvK<`j0U-I|+WcŪ(PX 1Y$3'k%}`\Zs]nauU0`ojkνT\ Y^zp ג9{le{OXta@xݒ/9KHl1ζ4)({՞{qU`ӕO;Xd?,q}*2?3TSÐGmQ伸]OWPsҒBLϭqdA&b͙^s{CoUlӓv;!\_㮄41:QhRO^'>X;18jD{~V2[ٽDAwEkeI/l>mrd@bUUO.-rͅ 6q U`;ϥYaݑEjzU獶8W5B^m-_楟||]mbuъF\!~ޣ#[U%pQk#4xNc>凎Ҷפ,rr[jxW]m:{ vv2yw=лjϋ\PMB6wR|S-Dljڌθk!w ![wా*wJ\da }"六 rUͷ|r)cokip[5)s)4pRX_\Yņ;uYa`kfy:ʴo)mIqP{S6e=EXz}IGmYюr͏[\5Χx5H\GUe/17)J/s\w<_~e GM8_TA:t68K/I=%,puNb cR^κ&.KGOzݫԘ@P7ϊ9z~ڽKJ֭[;<̈́JB.࿷\"o``,>\Ǻ5}#p˧G-e=p+nmya= s~k%=1h..ӺRFw4 BR0]ͦd9 ^uZɦLlt*Ybu\3}yT e/Wy|Ks/g-3a}-{ ,mby-hkLdwz"G.[|S{uՆq,*Z2fhT}5:zC >e}vz'l\`KjgYƶ N uW؅[ 8y;Q mv 6 )ZZjlK~׾%6Y&Bn1qã{w׋̳]~<"w]}c[U)3ӏ0GW\'l"Zbaq<8$/9a {uL 8AE+]^c߳sNV/uB=ץݺ·nwawC,]E+u!FtMK(.Gq}$.Y+I?=>^nJq;w69dcKJGjyIr+.e'\alY}NJ4Wqc`ås)JyWk^^!_iZfqU>,2<#GHzP=Q"wyJYz3+^_g]g@qO/ts(kqy㖳\9#?IyqQSj,*\VbwW`z+k=J_Z{wY{6hD+='{_ i _.20` ݬ /& D=PUp2٥y;Qot47]fIύە1X}RnF'A * JQ-p=QsEpdϋwenQg0ZybO\r<8}es¬}ꖥ()],(`ݝgF8,i78]QQzݝ~weLe^&.J8./8KBH'G4lTvhJ6y;}ܾ%@C=Fyݷ.TXHȨmR{5qWnY%*dPHvd9<~?Щ8S-YF]N!G~[(}.x;Xuϣ8Q!rv_ -Yz1Y⼆̖kRU%e[ˌ}5nVPDz*Ur{Kι̂qpP]Uq6},t_ɚ=Zj|g\q8)eb@^ b6{ 赇m+32sS}zS"W|-nY/YZ 8s?Rɧ  X{;-Z;Lgm3d WzzGNU֗׉{g{jpDX>;YɶERE90ph^U]}}b?հH<[1cMܩT*ټcTFݼ5;iKUL9 ӹtuj}|̛ 4F Ia.=664RllUݽ,] L~ \;^:/&n_xrgWj* uLӿ%cPOF*:^k0-}ꔷNCt/\q2aNAwmeO|ctgXxv/vK9G f&rz-u>>y!n^-R!Jf66͖9jܨJZjz CXݝLJ[^Rغɐ?+`wO _#!WzKOvO6gٜZ'ܢئ 2]]A kе MMGsh纗 5?l9\GrZ\ܷ>J j߳/ Y$ۋX=p1\EұLcr*?BL7.kaSˡ,ˬ;պK1ݣ^l|e= o%A=%ҵaR r:~7']Qg Vb.)d#'+<vm1bNoò*>?(UVi վUCSqR>lQJIbLw" qVYϵ깊Ke~K L5{z<7:]XɘTznZhw?A⎜x-J/:?۠ޝa˷bǻZt/gCwyqrD&dW;`^(V'/dկ.#2.N_0yNߥFJϑ1qtgxxS]qrq \H};GcVHڇ˟N߳dCQYF@ MݝR y_#\@O)\yek4nZZbdjɛʛ$j)V4K-`ꞣߊZ3^'s̋Vf`ؔ?yWm0YP|ox3W⁅5 9Z3ܽRqRz1r#`{MM5IqhPpZf vfO3$8¤'bUUh{D|faOS7Xu|/q.r,˖[Ir,.+9ū3/c!~^gkw=zqųԁJ; 6+okOy*.ȣ} +[re ee`΅wAw :(| Q[/F{ ;Mxkn.=C(;u쏇:*c^=]op<gu=tՓtKA|'2WQNsWxp{[#Zdog,~^bBJE[8Drzr;CP<'C3AlE}"&Y31*=tދ[Tat'Cwwj>}xӧ6f- ZO2:2 HL1;j`o7pikD9˹Q s{^8,q'$B#70BBFaKh%yԮ4~g^bI[۶-8A*3/Dp .ƾq%pɁ́OD>sEl([r}sWhʆφ&G3FC7g㒮A?LT}, _/|_8~k7>Cfヨ K>78YT?A?{V !I\˓\eE SsxjuPCu)ߵsɃkrF?9=Ivؕ;jW9 _3fYѻE,b ɂ܁Y*G]Wr~*ɴl}D%?傮oenߋD'?K?jvh)d˂yE󴢷.8.у0I^vmO.fgꄲ٬Kbԭ]wb}hE%M'5w uodwz]"C𩶏#mx;k tmo (%ڙvD~d!~ ׅdu|Uj*gF6T|~u 5圣~Y|I^RtQ%NM)vxT TniޕrdyF͑!kt9 qxcs+N*|vCr׏gk2-.)\40sB~bt|'N!c{}|ݶyi2K?WlOt6*5uzF>w:Xu)|%ẆW͸Je q冃 !74qzh]ou}sNZW21Tۉk29;5iنG p1/xq^V=f(3VuχOjPe.Է6_ .Xz2G%:CZK|x%Wv7Y7-Dd~7vlVc$mf b .*ɋf.>TpJ[jY3/_/_lc؏aaAaa2V3rؓ( au\ dwԟ;$nٻG]rm1Ϫ :辉J/fm|ۆ]G$bA)YpFfzX_R9,vGUÎհpjXFJ] ;j-JX8] +HǠ ~]D{2ϥ+ӏ|sM:ߵip6]f%eUh&pչث<+7+?u[<]/ɍ6@%{aY˿Ixy_B^J˰Ӧ̠\nw2W |kGԺI5T{kWPK 6|;`4̾b} 57m6(q ޸߱s>DON8).JT\g4X-PSh؀Bmp[᜷OĊ{X >5x?wxK·iaeT~oΑ}$W-C@s?Ec{U_S~(2 !vgDn Q$X}#oد"vh%Wv5;XJ.9v؏ew76},c_}_'hiׇ΄^eTE~7c>+Dm>嫄U,6E0MQꇟ_xnrN[_ 3Dgx3D}7>>GH % J0 A/n2ʱ*Y`1DHպ?6=I_!;&}Aܤ/w;I_cߍky\OZClb ~E@v7);c4N_Sl`uR|b :$@ hU-mam9f ]$9/ GeDxFN㑳ŠfC'xަ+ DB'< 3YJ fD!*AgS Lb~+g;V/=0 +qR\7aӿ#⟜Lǀ@LǀTXzLL^=A>cJIBlЙb дXMY`Lf{Ag {+h^@=Œ-@0+JgR0љT bf7l R dFsa=B {<*Pt&3<}T{!p4SFIw(/0ʚR% Mg ⁙TP=0-~&7!*ЌQaEg2 NJR= 0>JQ{EHe 2y#>V Lj ´}0 jaDA8<@߂T<g[0~mA1[0LcadFu~ F0YA*+8v߰*fkgp̀t,q=SJ60Jev18؈ܿNhkM.BMu+T ՐN 0tb'$iĀЉJ'J#*F}>FE@'C_V  :A ZAɖ["%ND0.t%|0 Dh%L<`0&B+ct2&( ɘ(c2&@'c4j,5,:9{?=2J:ŠRV*RJQZJQ *EiRN*E1%otL$SY,B?::YE(*t(@:W֞я>AXxPp)v㓘7:6-QZ38~3ci|VQzQ@(he}G/ EA@AzQ1PЪ!^ ZE4ыZE#xZU4ӉAxZe4D ˝x ʝ܉N< ˝x ʝ0܉N< ˝x ʝ0܉N< ˝x ʝ0܉NVN;Z; hN^0;azNVN;_s˝wr'^0; r'I; hN `p'H+wN;AZ@/w ʝzNV$˝ wr'^1;r' ͛N;AZH/w ʝDzNV$˝ wBr'^h@r'^0;r'I;! hN" ap'D+w"r'܉˝wB4o;!D/bV EPB`:u̍섰^!ČK:aeL+Q#y {UAc5L+Q? n$x#UzJIZNYLIݞ4r@_>Q,MM >|1jr6 Nuq@#HEajtd_If?c1}$gqLm‰@:}7bh (5'`\/b95'b-2ɄS5Sq< M5&  Q Lmo[Z6'M࿷j /8L[ ǎR<p7J9= Ii#0ť1ɗH|9j_F{p|d{ bD=`2Uzj{Ǘ"(nA4t B _0F@ b/A2I$kA?5eHm'6$`-B $yH[~Y[2rw<Êx:T#)Jwk$H>OxP G"""4Z$놐-Ų=t'ܨS&$b%D $YI]Sg"e%-+ɥpYVXGV$qU<0EY `):,ʣ ST01ep(4@AqG/d(a%,Qx^jӣ,a",LQuC, X'Q@4AA&_L?GqmJKY<KYQb80EeItcz(4 ҃&_," lPucQ{Q$qi0x%SӡQhBXF5 _FT(~ 0IYsA AD%ێz%͔}T)1]`0}ЄIY 9KFe]9KryS,A>lq00IYq J 1}QYbxΒqccQ@I>)KT,AlhB҃,ALҟp)KX^4)s$qDuu:,A̸$҄H wU4:(8=:f1S,0+e`1 0} hE$qD}Ԕ%Y)C )K3@P )KwAhuPlO4@O& j-)#%m'G҃,!Lџ)K^ szVV ^AI>l!0zR@Rf4AARP%Ш,uP7w,uzul!zR@R06 &DzP%fE4Zu#+KL@O'NbY)C )K3E  <($e c/XJE`+KʡT6SNQg? vHR;&k`IYbv0Aas !OSV/F-,*xz*xaZ)*K"IY0Z`@AAR_", %VS`ge}TgS0kg$eIK& =(H J鷈W 0$V S@$qL-bY;C$)Kf7Y DzP%@E4Z$,ɑ2US`g}TgS0kg$eo.&(t@H J@ileIY+x( }0Up<f BRD̸o.&(`zP%C"DE4*K 8$GgV|S`W'`l+x,L5Y (=(H J$QYNPCY"}0Up<f BRD̸o.&(@zP%fokdW `9;?&՘,!#S~D Ic  =(H zd %vp OS. 컞$#P匑)zUQln"҂b" iZ_[ET61$RX GD'TYctoA i{nn&( zPQ +l&k7 EH  .\cמ$#P{tr8na4@肁&__ah xOWi jtiJ,$11qjn,$_&]0@nah t 7R*A' @Q9IGr`9C > 5CEV{ًVa1!3 IZa^^/h2YrGck((rUC-f-R6HN[oѬx16V5,K\m^hÓ̄lmr73J0MǸI۴dN_wёz=;x79{M-zͺ$#F[,01M$4#bKzRx7Б^xh?c"~ mˌK\2:.-_11%N ]+(F_Uu5:b@lH %&boߊWdt?^؆9_/}OGzo<:vߣD E`h{clF G[?fbT^þLQs[#֩3ԩ ea$s (?ɸ 5L'kݺQdȇ-~=[f^>(Bl^f5egٶD 2}+oL(>y}xSzc? c쯜)6UQ=\Lgtǵp(d{ny[Sdn N^Ai,݇ttHftSk!7 \J̮NrӈM (~ ޡ76Ln x{(-x|IKrUo0Y/W%xH ^n(z9 &5oK-g@˲gƂc=E߃~?| N ׊{ )6)tpd 5V.|Ai<4ӹSů2y CIoe1W]]'a}"O[5 >UTJ@pQj%JI0NS5 Эo:n{F NӌdY~\Wr s'Hsk#u{L&6kUtWzO %x%A0NwW3/buzOJB٪!/􆦵2%C~6=gKtv1],e7HeuD; XS#ӝ3ҽNb=52ۦ4r8%5z:G;׷mq|yç דZmfñNW=?kZ筌4sNH=w:wln4 $%f'C7 &5ONWGnNղ8u{5r\'QiEz0;i@:9O# +S̒)^gkԇA{ 5I#%^kA9 La/*,^+8`䀩>?4,ni$9`r@ЈIp5%I0 r倞!eV0I0 r倁!!&ULL0!! p 4 儉! :'H 0~ _a]9J+w+++WpZNN0b >5cR~R5Zv7ͬSRxLe84U9;9y< mũKx`pA8\L7A/La8uY}/>c |R]K{[QaP?dwȰ D<r2=*i*=ί+ZREK6.조-M٘ƟmԲ;keeSZك/_aL~p?۵f T/<&l 8v۱jAG=\G`tjb ^b!=dfH4U&WunnRG@:(-tņ$8HH1{0mF KԞ ;ak^*vtǖEFr$=!V[d!5ȶȁjX@7=upW5 k{% ^|!tA45!\F`;vO&\6Ь^d+4ĮBlY/(Yh⠅&b֋]Bj k A^]аEww%9`QBWi8_|gf1}!tg$9`r@&sg$9`rlpl&A024:\e$q4!p$8 r倉 s5CJRSʝ8)ȀqWS$phA3O4V~f敯 ^d+*^XF Sa[֮Fz;v0 /ʤ85y`2%  \H7`|4Xu}Q1'-ů 0p-nxŖtG!j͋xX ),R8*ڂ.Zn/d_*cRZR G߬cL NRn\ ɰŅ϶tB9xO'FllEG=#"ٶ!5v Fq^Da  ۢޥ($䀈=,)S" @Xi8~啯D,UyXGC<գa j< `"AeXA<`moq)Et4zD{=T3+v=Qd;YD;[l-J r?~,5Gw_e ?c,z5JGwV'?7\(@uNb0;5>wrdyFsUsl}2KFsUW䀾Qx>p-<8؂#.L(ie޺k A7M@"gNZ33)Nh~2ƫ[r=D#6ш1f?3j̤1\kX`z}*ػ?HfN42|gCl 6ִG wje%P!/ F"8u6{E\8QSuHQ?[EE2[M 669( c[(E{ a`o1~@w/a(IRaY&ac[<NJ<^9V9AQζD-,g[e9{Q,MK1z /IIJe c]-gmb0 HHaU;7iV~OS|>?MSvu|Ӊh5.e"u"C*Ut׉6-xEL7Yvm9]Aœ?(H &aAڵi/ nLv-+i a}KƦC|i{(H{(H#~iI{q-Aڋw"ce\65ҷP Nj'r<}/_>T=)3<~{)5<~Rtz =i4I)}(xS`F*Z``? 5v鎟NUh5GX9<Ѩ,GP0)DD8]*5jtԡxT`hU(6p,DS!|askoEB,q8=c&kfX{=-Sf 1m+'1k5Úg2%QnM0C2=ԛ鬊11 d(јd0pz3ȪE^G;ThPDPez)taU&V+@$VgQVbM .63?OiOnAab_V3Ldbb@2ֶf%:7G8m43867 :45>"'^Ό"75[jQpdD-;ރSt&+kLyDVYk"'"w9usӺ&̍VH+Kb~H":gVp/눂Ə^\uZKO;Mpe})V.1cQKw].,aQu*y|6b0&gb:ȕ /0j S&ЛQœhWQz`R r)ׅQ^ܢ?(Jz " .>($+F yHJd1q;F5Ku39.$Fyӣ@: Xj<,?Fh,`n2Gqjd>nN/XMB[;6toJL9a &\4/ِ)I7f&BoVqN7:MMBcq U|ؤQ3Eaw010^rǔl4烼nm,2;RI)#2dM^SmPL\kV( i0ao۩͜nzE0_u ohA T6tجituLz#;FA(4Qͳ }XwvKcc %Q45boEaP-9tmlLRmIV:}6s7_}̚a[(`5읨:w#qZ6&QөF5ڿmB"*\4ۘC 4t-F nܨB1B"Mݭ%±l ]KksF}fG^4!m`dBZ7LCFmEEI>>o6^ )ЇЋ_Q=g$'˻ F; H6vp1L*LZ AZ GPZ0rw}v 5ʞԽ!P?Km4g;sMrup*M$@I+!7"X^Cs*/ َp[1U gfSȓŠn2nVx`n\Ţkzο0z EѷfrٖVGO?ɴO^?%k'c'^b;5{UBv$l$:^ފo?׾^-Kb{Gh5p:-Q̓>!,lG-y zkU?<(Na; ț8 0kGRߴ0?* Qt6DqP8 ' &t햶&0&pw/h=lnT;܉f^12aɁM&gv.n54bI+d}$HQXN L{Gc33 EPOa\K_AEDtcDPݜ Ԏ6:q-Iy; ;/y?BoA.tz~ӳFw?|_~㟾=0~ ;;RZעWJ_lug1 I/Y[\m'ɽƊ^㼵}.D5@N{bQ mx1?.+Maۯ/6lQ5{NfKeZVqc3?.`8[-]6{j~aEqHڻ_+4Q3i&YTuc GN-+)ÒaE?-nxЉmݮUG-mk]wXP?`{w ,t0OmE$ +& wUF'ӡm*I2ЊwUmW ḤIi0"TpZpӎ8tlhZbgmu{,HG^^3L Jp"d;WIN%^APLB7jt{q- :iI2p-]%N2t\s*I@1~_>fv K%7yw% xwGE/\%810:<ܝÝnRya:eծ<~ /~_)}JA>!hppeFixovEBL'> ~7Qb!; N,nXYxܩt@6pK)'[ &aGfݾܖfA!͂(F ͂(BRQImnf20779{{"M΂(ޫr0꽛< ~L 2N2ȪAታhH|kx~ 53@)($ i(QcͶ`Px(R=Q~S'i4ciқd`X@3#͌43eKbH #l'g'^bL^Ϛ1\2͉uNLbW j3Qorvuؤ79:FEF&gW3zÁM]MD;&g{C g:#*ARذ< kFrt9u:Gwh[1ߺQBWb<% +!2}UoG%GQݘ&GF=?I4BROE׾er0{7g[Tijm]X깕įĞ*֏+g)Ll@CF 1 M>uqqKOft-s\' :]QеI9ݘcKI *Z2a4q+C`UZB\yP>?,SsF3)lGbhVafz6x]Yvk+sR WLo!@1/MlH*-rf ( pkt*q~r4 Hḋ]+.Rj<ޒ)8NG1l+̺J]EǛN̩[ *s؈æQn2! "4&"d: UԽ"{׏fm ѤU .o_պ&2Q^mќ|&D673즤6ޮImnrQ9,a1a!l"^/X|f dz%67mŋNdÿ] )K`Q@lvsV80Cdqnz32Z^z[I77TX6ћ1 :ګQg(3]ϸ~#ra0T; Zj+_6pR 2 ,%T4C B*8\W!J7 `AWd+@orfA uuY>QNorOImD[uEѐ8>293[׹5VGѥNFY UN:Rl(F;ۼ=nU/.JBQ whǍ*3^?m}5U+k׎,͍f:{+9̣ ߇^;Tq}93(~7?3 N*+Z$zSuSNg c'r98\Lkw.L?8+qv6ifjz7} ڛ}چa} %fEE{4v9sK[fKeklv *O5C pJEJ bc/g_nj_Xĕ3Ov9G}{&=*TӤ~ڿ,C0* `Y[6-G~mK᠙~c?_Py{nғNK;(}By& k|Q~o[Pg~(|~Z5unjeSXl`kmZ'&$xidOmfm]7?)sA%Xi4+ "<ʆIEqUT 1b~)I':|#6ӛ\AS`d00dFud qfTpWmJ,~=7|芊YP?u^+D QL4]Qorf6Pi;磹r%gE$GѓGh{(ԙə ə@휏VV`$97/4>.2~&ə4xJ D~5L *nR`)UЁCGsh|ҽU[" T+tIGK\/ͺKaFv-'kSY?\_=h{-šRN>ytuQo&Oz㌂&qF*^WvvlC`'ǎd+8#Nw*dbE̎d+|DA] GXQau7J{`kU{`EMՎ2;_E79oRO6؛,6H&fI'`4fmPrwޓܤXG*~d ?3c7Hśby9`o;BZYDnjBzp^j`gMMƂQ:DqT c-6t3nr%Pt{B14;‘;Gۋ2kC`R8idAe;N#! "9m\ǎ- mʋ6%!L=@mnfVi&tD=j{x0aܞfffbS}~F*W o u1 )AdT-GB2*fzoћ_*Fb+] +fɢ(-xGŌ:2OLt!qDY8&Y,DyPא T(^tH7I1P7 1P,؜֢=iqOEJ뢐N2dt:3aKqh7YNo _7RP!͏?;T=M6H jmP4FeesixjTqc(G;pzS/hTgQ@ͤ8ݴ I: i47Q"BǪ!$Rcw3b:ۮR]кF̔IP˹NO*u DRpo*AdJN/b/i$B<}ha#( c[(c'(0F1أ9=N-Wޡ;,5-ibb?{{{laxKC#Pc'I(A؇kO ػJA]^Ԁb5"Ԧu=Di@Iz>iC$L԰\a ?]^-fg&ȪhQL7iBl(^ץMr ⧴#}V_ۗסM2M]qR:{KhICoE5ij&VP_ItVƖblro]Q!\pԩ(:P575,\ tt˖G\ 7u"\<# rTYnOhĪ'Q;ю*nz""t%lV{k=E$zÆAG ͌|IsGd-[\F-uO͢nL6 ?f51 yYvMܰApPu 5= 13;6D 2khMuw]#᠓&4oRGs=bvl#QxəC$3QDKbɱt _8Q#v rNăZxU yO&d&7$@z`chmv䴦OZ•m([ jiX J -&O,<ڐ6 {yuIM0[w$ok8 aW5 KSԛ|ok@\4ffSAEqcFMd['QML OtڲQor&eyOs69J7Fܴ"2hh^#C&ބJ̽;vQV}Q-=_{˘e־ _js3; js;͏\R{Җܤ67DF{# 4~#BzSLpz&!xVo Eq% qf}~̗+HtubgB~ FNoo_r_gWea.JѪL7NO[ lq ,PfO[=>O*/#؛(6*V75JO^E'`}t7ĖfRpnu. `}r9ĖJ"M~TgyYva6]o?~9_~?}-+k,Q(⨬\X;Q# D9hF 69qlt:90E8R[^5v  qzXEĻeqJp:/\gHX ՇcR էh *7}GY" B-ǪY"Sh?b^B CSSY!њB?Ƚ-kDxmp#t }i> }w֫vaE槎v}l$FDC?Du1Wِ0Rl$Ga~ѷ~: Y(}]b~ :骿iϛ".%S`Delo$+<7MIʳz ^R7%+<7MIELnJ*Vnt79ƴ6zn$߂Г7:f0My\VxOL8y17k~Pe?a؛*^GdvnZWYp:Ab/Pn8PZM;ynLg;nI7f3xYkkX[-9UGqt[3 k%.yYV<ˍ9L7%^TxX~Y&ӹn UN{$[rUb I:0ʾ˾r;79(5Os9z"Z:9vt]=7b-?sqZ=Ƣ8uK*Tdӥga0 x!ư[zxXy0&ɹs\CV xHih欅dKpeS0nPe鞚 AmOxBS%T3|TIke3p*8p *Ѐ W `W1`U`Z ~t0Z.-l1Y#8L"l79Xs*Wi2V F1<. Mz3V*kH#%Q; vahLFQTmWm m'F]@b.9WQWpH69WQs*hr@.UZ_KghQgM~HU܊Ύ:eG,%*T`EF%=ەU6M\&Vg kyuώaU,:;Zޖ3ܨ\红LIV讋^&qUtqlr7906:ȕ6Q.sGy#&i69KHVEp &:':7\ױdU~*߉Ώ:u,@#3UQ"#O:*dFSuE_l%KC6YQlϡ<|5ٛL^ŝdSp|Q  ,!,^}V~UvN&Wq 9Ӫ f4WDŽr5 i5%X ~Z9-ڻ&85`FE(n*kW^RP75!A-%}=KoF& rċ™8*0F#nyTdxYxeG{ M{fכE*vˈ7[FrܭQ,.2LϊIorv/Iorx3i *l"}#y+H*uhM.PbMyaU}me\vaWWsϥmIWm&q 1&3m;$V&rh0܅o ZMISw%"252uJ,%/R6IU^,o\Q2%NHIxXjٺ$cYhj (g#1(3ȄsTlD01J]Ha~v@:*G٢?37HV’ {,m!k_E9^%l)lFA.ˤBE^0& 0]MJ!_}xhC0gh]Aor<'WW"wzi -dF&g*jRQQK\ X^F@J)LԛE&'@P*M8D]0ꂡ4V!@Dh & "AEq :D^Fy]賣pPBC)⨤*RPJQ(Ì]k ^EypJy"M ȫ*TE>Gd}VyQ<*J"M+GgU+8 1?"Yвs~@2p7CQA&U|Ϗ}0Mq 5VQolgw=;~2Qh}Y-]pTV3D?˟ӏ>9A-R!̲؛@* (P`*IMK?~psp2-R 8[)&ܴY*7Y1ܣ@Qg*Sp:tT'\P)nkf ?nըL^+7t=ϵ P g*[ 44Bр ߏd )oU l &Ńi Ne V0xM83 psv\KGzx>? 4nÃl6|rO˖șo\J7s45*⦆4x!͖bꏚv* fv+[bVCyҀFmO<#% Ptᩬ-ѥ-`La$u'e֭7r*~aZxv,yNNo~Qg~RwA/N)2+f`8ZujoLl޾Ⱦ־Z3*R[l ,v;NNߒontloߘluȻf־-%s ptTYqbob f:}cA)HݷxdBcn$Q́[GqbqD>΀zƵ)mWgEl7X͡0;ĉk;'7Ѥt]Ʉ_}^ݹ#k&k3&N>òk&NMEĉ}4G}W61L8l#‰k 'k3K'^>҉kI'^NED7~T:]0dͻ;wD<-dmDcfˇxX>-D̜@Q>"s@ߕO P;"ea ŷ$/J("vhB\Bw.PBSBTD1ðZJ%pQB "sЕPK(׍'ሄJ(P K(%QB %1Gc1v%uI<""ea %$(J(ᢄ"fEh<Ʈ³uJ<"Ħea %$(J("/h:ƮzuJ<"ĖYB!PbKB/J(b6;ԕPxnCIG$ԒP~^"94,D %]PӨ&6tDBI- em=C<,D %]Pܱ[~硔,ńdb1Z >Go,\- Zmx鐻zm)I)iVs2qQۿ?ǯ?=oy߬v⡗6vZr4MV(; ꢹo\Lg޹ut@ҵSK]Ck3*ک!C\kVIHfPҵ+t4Y^怤kMK]XQ͡tiH$6tmg ;וty;\KXY#b7s'wNth} ][e8da:s-[GY48 \W@bNj;'3wwZG)~a\vp C&=&g x#;gF=M*RӌhCvjd31J?(/T"~VMRf%$[ҘFR'mg5f:AMJ~شPn.Aթ5nn.&ѮIMy[QJj4Ts KiÝ+R kpBmy-4&nǫ x$D*9];x&7kĈ 7nJ*ٞn _3DsθYȪsDCow:l榖]t' fjzEMQVWn9Ҋs sA7;XQW4j0/ cHc *;e^BoQM=QמT̲jLT`$ˈM1w \zkO(fd30}2`jJ(*7p۶_6 ٛTOP׆N lUlO2\-W8IKd{5,j2؞th\<2Rմ.DfvvvrwlTnl[2>F jh m,Z n.p lYN*FfE(naun[i Un:rӡp=ūEc#g")5tK+bDӀ\~MNr#:'DDnE7:=g۱‡qpSXhmO#;^'NɶmcU:{ 6P뤆/Ts*Zgy^?m*^?s^?wӥkmnjv7݄N&\OscugkenAvxrmusol07:ϫxNM^?oT(msUê1_1tb*[~LW!t5}H^/r WI}>_b$a.᪮γ*Ɣ[;;iraxH.EQܻx((x3#e:1xpZ"lo2HB16]&|-V0[]i?иhZk^.+ĩG1]!+кUdy%რwoЌvzlD"V>~ }lr]\ek R"cO)ps|g HbC+5(p郠oIajsiX M-)4RhУ A} zȘ)޵@ ""^c"c2=~ʗưoI^Jp,_^t-P]d,2?-챕T4L {IED #cy޶KcJ^^މػ>>wzt~؇JI>\^̵X^~ѮY=:?ܞʛJT\SiU[*ZyaQ4=Pr"x{QXZ~ӮőKcJQ_^\C:od.m2ָ0Dw\| ^Xr>ɹ?=&0iPS/DQܴKBdq:hǐ;4鼿-؎.,ĶMFX4nA)};%(WЎ/&UcbD:בQOL'/-y|45nIg:m r*n)q)fX!-A:An_v&߶dܕ]i}hnVY2S ;_3l"6e57h;ntloX`ew7}sù,1u3Yqbob< -_߷xdBkߪ|²dİo} aQ÷IC样o`!6m7.Tvމ'x]wSb]k8'c ,$eml#)H>TX f?+G4/}qh2DI;-㢫)2K)A{Nǻ:_Y+L*vӹ[:Mv*7}^o79hb- pSIX ܄=I$i5 вk9~vewI KOS|/?O~~[?G֣>Ň?”G!t$_ǿBs)MjOPisu1T՟N[I5pSiW:hKxhV= %q\'4Zs60Teh}mqԘg0: A$QvC~t}v P`MqM؃`U`j>1La+)*NL}i7.odvs '9Q明0Ǡ~12e QXU|厩TI=Vؘ/L1L]ȍ!;:6]p>dWaT8DPYuXRQ GK!p?Uˊ9i>dZǵ@vY1GL@ 1X(4}Vor&WY :2.H=@Ah&7lrЛVEt}++] (B".#tzȪaTH$~"Q~]L*$*JDQQ R 2]Ҫx]J.)&Ѥ`qۊJÊ`됶!\&vіZc=tӣ-k(lr蟵a2O}_Sk ,&;,OsJEJQ~SbޡqJEJ&n:W*QYk[oȼNO̮P7.n,.=1EȂ;V.8h-f Tܗ n\$XJS*ԚwP_>럑iT\k. gH$ՉR/q[QED c*P9<qVŁ ^Iu*>N8  .8kE ,@ XgPqyEs% qcFD⃊d|PQqO>d~Pq=|2T(8'cO27H: (6 iO4.IST-)1dڒ)=Q űLkuZ2yRJu.e E%f .q4% ."]'i\~K N!XtN*s-W)M8~d@я2Ge,sGS ^~;ѨH:w4E8r=~kTR8c~&#T-Y x,e <ZmʗFR%XAHTCcVS?c1@ f!ia/`a@>| }VH M*]()Z&;>)M*^&1DS_C>]p])ɾkлbCj*@0;} PXvc %t7؋<-Wqba} %1%{~C8[]˗ƀnA"6 .bFmƷr#"S',ckW#4RYI.;AkgĠNvLeq;J#W,gyY$b&]5=iG[qD- bHT(WB4"1qL7-m<.H,иM%ܳOa_":[)Mr[:WSx MR/MfDL?:A SIڵ ghqa9?LRVPZSVhJSVD*\UIbJMYIuʊ gsѮү%KB*_bPY9ڍa{x0Jkdu<|a0 n\* @gNdsi'tޟ'7z31TI2 Lt:Ib4$ CpGE<0d H%BFQqqCsG,ܑ(i=J>bڄJۋP"AqH8.{Y229s4H ޒPF$[A4Hx]ચ\$Jm8D6ӛ̩tHZlAj*i4%a1z¨793HH+ICQL"7׫tIx]䲕aE^gGy5$ *JmݪHVŤh{OC:eK*21۩O$uZhC/';KhBQ bBCMhHuBq*๼- 8(5ѼlvA8!\^Jh^Cd8P5la41!5:A)NE~:ְĴ4֐ԃYXd%5Y i4!.dS'1!&7$eLOv*W`ҋ,ҏHeT^EVJ/H?"}E2D"WQO6*1"QN(h¤4b : Dh?Th4""šV.iDs(8 ;brJQ%Q`슃uUx]XEy0ʃƍ^EA_Q j6>)~ ԓFJ:ub˜2&Sf **/f/m;.Z܇R1V{̣s7BO7uخBD눳%%OZP)M:%MT(MGS>-^ WilߋN2: pw'2kRi444VY֑itiFT_qՙTE1\Ѯ54:uׂ1Lh~Dᆉ*7~ì5\1UFΨrǘwl'K\dܒTnsYsbRfk¤rϘLU|]I1/d[UCTnsIU^#M1o$4I妁IC5=E3F*nT(OkK(Gm)hw&&2k/؊2kI:ۢdG&PMT(ƖYNqR!u3E107 3r,k߹EEDxI=W0X?  uBa0 bC` :c0ruv0)W'rKkz05.kTl{E˂9 ,vYd`s;#۫͝u־V/QP}~rV79(kе}vLxN]@MzۨXNPCUeV35*( Qsu:fӴFKt(sa\1*S`C`DQΏr.XDTsS ^(׌ 쭸0 N2P>?2?|<0L [:U }Z)o#<-3֔ ]79%JS/Rb79%pC3)]G^Iٝgb'f d Aeu<$ώIf^%~`f^PjCln@}̛`TG +MhSBFz0h *fPɤ<:VYI *fSII;*3Y8ƩӸPL0TE')')( Ѓ]bn:27IIyt[FۃMQH?QdqEF?~iA&^Xd(o]ȌiR?b.V&үaFdzx0C7H\ǣ2m;,Xe^'7|-N kG"%wJK0S$e:?}LtH;L{CEۤĿw()'>L*r6>?fkw67Ϋ oK'(.X2ŭQ 5UЋzN*ʜCDl̤AZ2e{ʛfʯUySf(>n2ӊFiLu* ;3׵p'jD^쫲L(zTp9pӫT(0dM\eR7]iFJ[ A"MW\i^ȋbL  u~΋*Pmb<޴^v^7Ty1fO~0 6 &Ts1Հ3838*w0NaX^ 0( ЃI_!T0``ZWQx^!V080 T`a " p0:o 9EhGyFS-0m72]2f:o\sFyFㄽV0юFL *{75˂9 *+Qv2Nuy#w)0Jpt"stu#OS)hw$G'rG7];P)Jtt"{tu> 'Ms)0J0-MڣYOzjFor&T4.]F;Vbpɫ*Kh:uMn&gJR3XIۥSY:Gۏ⺀$6yP|*&gwJ9W4Tp}~UWR~<ћT** % " *tY靿c&gF"Z&(J įȯ( ޳ɃR5R]XdFsL]@uzh/1)Ɍ&%_[2RiJnTez]EˋxJˋ".v2mWp RĢqG-;Z429vU _.*4G*KghQ^숉*EMΎR"%QKr];kbRۤW$Ҩ-umnȄ"R&]JO- k&z3JsR%'a$!M2fډMn&gJvkgFt ,4Tä|8Q)̸R&^*Cšˎ,  (V84kJ*뿮5JfnҐ_tJKMT0FEƴ! 7Q*5NbfGPi' ؊vU9gdZ*wF]+M&“ ^ 88 ˾mN:m#3qtvU=M__}!Z~޷b=Cwn:f ŭNo~]fY$5ZԦhZ3}l}}gTdhLK3NN؛WaFgS:/TVS[R=:  I@ABqhuu7*V{^gMrlNZ^yQ[J_*| 0U7hs Xxy$wSl*OU4{)6P4Npo nTRuY7`mːQVK`+Dh X.;\PDM&bA;->R'}4 *6M*YdpNⵎ'@EN® >k=G]xDΉkLO4um' hxt-Nѫ\?k}f@Fgj7_ hd`*RΥla\^Pӽ IQ1˳v+'߂z_bЪr+².VyQ3CԕdH*W!Vø~OӖ^'WkJ=o~*0HCwL '\_t%16NNE3XMhf2Xe-=>1 F v`佱[z˸)T0IXzE_gdkD酨($W {>ihG8%Sps8eڐXRl` ֹ?ˬ[ ~T # _yp ֙22 HZImnJ9T 㹖1jJ?r,IS"`js{6S *%\ח톮FJ,]_ NQF]FD]js3/JD&XI]JY9ڎr,LK]*u@׊RN7h.a#*e*PmNc]mMQeg=}űV&hb +nқߣMHF&c$T)Id$2- %AWwVorv4F@wVБD?e\uv.@w]zVq\xU4#ҌX@H>a.%LC(*/7x7QrE>G`j`l *(d 馛7cIySpY2<N8~6dn"uu֫Fs V掟/FDmk0cZ?&|~G<=aAee OS|/?OћO_Oa)rׯ~_:^u~F?M^zQz?UDMU1ZA8:*z:@nNo)7 C;⛔dU(7)ɩPo.W8 t Bp T|Ogl(6@M錐-FΞNR,p7ⵓʝg; vR ޸(Vp*wPTN*w< `+4}9o;0-2‹$E.=.fC&smbVJJ5BpTB10=tBmpӭf ­v8얼[|#2)p]Ai űU==chK%T6),0{bW-_cvKtt JV *W tʝՇTBP!\P2; G+[7Qs`82\ޭ؂`92|8Ӣ՚A\]3R<3/:U]wLEˋ^뿖EE dl]b֕ͺecDž ʴeyLرY{XAz֪J`ֵ7 VHb>L״7w ]NbZ6T! J{ lU۵)t*v'|893\.ͅn^hL:^SvY]hR[~[p9 ͩ\BOnМʵ Ϋ zPޓ>r7y%Om)̼&3-vSP{:kK`߽`= ɑy:kKo`R,vM`[d=XM7- wSQ=US>T yTzX'yg[mRTܕsd X Ib l'Վ6TfԘJu. xnNm)J)Qc}?^Ζ*o;pkd%LfvJd[mgk9 i:ilAZm)P ih->Jn` dm!MZi_|v*HX|^F]Hh.-vVzlv)^ԊuHHC Rh?RZd! ";C跬-,ly,mT7dէImfG򰮓6D`^8No4 *4uG*f N7Urs6հxMհT( 7Uran& + VpXI_L}5$)XC\'ËYCT+\MӸd ֐t5ĝRfj+9T)i.Ue\ ӤrNxavpS#w:kKq%eM7A\֖\v=i0{vW܎ k:3#{xxy-WD( F ba\b_A\ :1ʍfT:Jc 2~.j ĚQjr[D~G;c9gmdԖCLZ ?ho9ԐP}+T}˝Kڒk8ʙ=Y!7 629 y%o"oٚ. aw:6k[׊9"튉,(9^M%'Z+Gqp]<^"V@Dz6#=d9xV7I-oĕ1/͇.ᄍ8پ ^m%VƖPR> 8G77w529QŤu++QmtCP@Tf(T٢)t[-šHb9Ar*I<1GW''&$& `N7 &iRXLM6a[;ɎrMؖb(ɀrMؖbrS5@wMؖb'PoةP,tt-ł'tÝnXnʝg;.yBr9sp*w<7Qr wʕla7V6pK#JEkө@[2 zt ЖfE,7 鉻:?4lK8ޗ~~[BXcZ``һ{ߗqey.<^sWxe^?_(^WG(=_k;/@ek|K]ӕnjx%?J}Q2KI ,yX W`Iǒ̏'oف`?Tp -@)Ajcrnl98ԅx YP3Y;X}W+:z, oP~I /P+~H|+C\WK GaҜQZ^?Js--=t{u.Q=JGi(ӣ zn5Q{X胧>DL$O$O$O$ O$ O$ O:-=cG?~Hy=h$oE9̑4sUD9aIDϔqcgC8P5iR2 i: 3@ =S/-KDyԑ?Є==i]~=з=?В#=Lbh/ !Mi Lo%-S$K=}!҇BG:~aN@}:@@^ -D } %0ч[>z(+"C$ұA:6H`"X(No4|һc#EO'd%"EːFeH2SKi=Cz) MvTcieF bY"KҾ[w҂ A C% iBT!Cqݴ?w#C:)qJ5%uP4C*!ǐcH1G[CDDZ!u8zH1G#0I1X[0,!Ɛcd(yb҉L ^z/26% $h HO2e01RL+&hwD^"R iT}1)O8RM !M)XU6ImI Ƒ*BO4C,}pWT,eзuxt0H[@P@?J[i " t^1R} Ӈ2O"R~+.'b, S77[Lt$D;hSZyiK<}xEv4kH/-2Od[Ⱥ>!=0aK["Ö~OX" ]H\zÞ'|<=@z8̑Ds#i -ZrJD< MD3'y9S^j Ho>X胧>icC*U@31t~ 9nHҶEjW@4! HҶ3qtZg<=C0:-E#!14s#C!!E Hh´ }h!OEk@__ yRB P"EcĨII֢C@:, 9{'e|z@< <1@T@Z-V Bw[ܰ$ Ճ@B!+C-V :)@)HQri@n? /CC.? 1aRftX  @N郣S?@:,KHr @nAEڦDыF>B g 703@A 5H;Rta !I%aH)-ނD'ݗ?X@ʄ[t%B>=Cȧ%XR%ѝB=DE@}@ e?gY%bVU"fH^(dlIAHI$$Vd+@rS" ܔHF$z?̆f64 lhfC3 43@3}%E?בDMT[n-=hBGr4[y;EHߢ4dX@' YHF,FLE֑2d@R$*#H郥>~d@ Y HN0K'o#}9WP32  a:E2 о2d@`qfzhw?o_ٿW??)eR(tk%b9yf*R7yB 5 C[(BJ< F`EWDM_DBC9;CA0B$%9(B7(zG1FK;PxO]%*J{II\t+M^4r-n1fǪK%r_(Lld&sYd[d,$; VGv4kb"+ [A2*4nR6K좃H:L>BjiEZ&yDI$D(oHnr-W&]1_|$dF2]{ $E1)pdʡ7QwuM$H%Amcy,([# &AԬ)v:&?BK"%&Â, jI-]!-,iiXBZSI% 8VH^.wK_|[)mJƼ55 T'/̂5IK$ XH $q%#N1AST'B.8G(|)ܝ[oxrufg 6 Ƃ&E2?O83tFKRt*P.ho{ASZcX8O,(rfc| D|aF!1lL%CE ^gܕʙ%_%*S ̩UYܕovL^dI!)u IJMnF^M#y|TP&'%!C L42t(pS2y&cu#lur$#Hf fBL,1r I"!a=RwH#%rɴ?V\NlW#<案ɭQ9," %\ %B)|fQh! 6343=׹&.62z Oj)|V?P!ǤpdCڼ!uސ>o`5 tEozyHK@K2*1 :o.zvJ`5rJ뜷,ն^};6m^ĴWN9DGf·]`m31ɎK)(|2 6mȞ`Ƞ`("ڐ bQ[Li-6-&C! !!ۀ!c^0<&.y"} !Ӏ!@@_\J2r.r.2!4mY Mq7晉aLiEd]0\4dgӆHFHؤ?O-7J-dY3QQ4Q4Q~r2c4d0l4nhEAY7 !!H@_{ҽaݒ'M3!?!!ۊ!!!s!gK8q)ڵrv9NKF6?27v9iڦk:ԎD rb7-s$!ߥqK::TʐҐҐyPཡ{CLFJ\qeKTVIt K%7[rƗd#H޿Y3ΗdwrTxZBx eYg#No~%#)ֹ<3!d3=P֓TO ʬGOI  t,t2L$/ Y L\rK~H/a$Lu L\Nbdy)d% e%-?*SfgJ<3,3d4T̐=P2C݌孞 -O2|VU4 IGr$ -q $VehhۗH$VFHXy`5h.ȪiȬicji} KF()b(C^ d)(p?ÔDa )ï:)Er^ T+( PA8J U@SDa Ȑ( (> c##xQ~- ~_qg0R #f.@DPl/@I@La)PeGխ 32wY9"@VN(iyfJ%%+'ʙ?v(_0'ܔwEzfld@Ԕ (P$S@A #)(ox7LrF(pc'Ofd#k5,ӑ%+Rq}HVP7rRWpzKě KD_r`%z%zgEYZQ~P~[m`V d_a *׹1%89rFn3g&VV=HHR*?? ˥h%vVe, +ZaR"lFf-l2f-bԮ/GCbdbkHNLLgl@b@)&@1b@ %Pyf: d 2)=@^ S/wA3!.a.aXni%%EflPlyf\YC~ж%rۆl@P (3d /2nY'#C~fR.A[1$5D"Jl+ErAfBnzTB1T80U * T>ȔdJ BgPdJ* d\2.QUJ2)NI`#]˄fv˻RıJ\Ʉ(w̍@Q}@a}@q}@e9J&3ɨ!+}U*Ay~A/1<} C"!CP!E!~d62E X 6(.UDߦIoɂ^d2 K9 Q(U2EP Peڎ=VJ,VDL:(oI^,W5O(2X'`DbHTb埴˷hfGD%чN9Uh?c#~; S:)K-"jDdD2!by%)b(F,jHP*ҪaJR~ҥ؇dC `D `DŴNY yHyHeS,zHH=4TtwH=HH;$Spd8K!L)uH:${RP#LdC2!YlH&8XF$RR%o3*YՔXRdbUSPVHI({RAO@= U-]N'F'(=zr $b E36RUV%Iֺ& ٠ Z2EX#E Fݔ@D5S~,9Į)RbK ÈTbf\%ql$V]/:Q:EQ `.ER!j)1ld9/ݛ1MђG]$%/?B[V#4.,A&VZ ,APys%2+ȱ$EF Ih kbᠣ럷EUs+l5J%cRpRf#ј k8VX&(ɂd5?9fUD#lJf0(k.e2"E2hdA ![a~G$[yШA[~U8+ȯFi\4P17Z->ķ|ʕsK}Lt)[^AhO;䗉hrTw-Rz-fT} "[1hqZ$|w[F &2W12.·\32tK^D>i8P"hI:T`Ns~AFo3ExC8'P=9%jTk{G0+R冷=@:K^̔RJ8-aTa죊o,$fҿ'RuA aJotTY|G:KG?gTa~ϊ6 $E}`%#E㷨*qE!,RsR%b eR~_?pBU#fs{3B<=A'I7R̯4Pe?UxBATe~UL=30a3۬PNP9j*̑ TOa5ojHcSJW8;L$ͬCf 2Hśf|ӺbʇfDcAaconEv x\Nŗ6=;=3\L$oF֑cFb8.9b=nS ]_?_Zw~_Ks]>J&A)IBҩGo*I=l|Js/I3RJ~3%.\;RKK&֒E$,Y ՍKT4ZlFQzDKW%dHK? />>XKm EZʉOd&k]A/Ւ>Y,KХ碙-u,IT&o+[mZbÇIw?wmg_|_~ߦ)ST>e;}J_w7Kϟ`FS7lIS~BI'SH)Ǔ3 ttayQˊ?YFGtSt)#?S("J*DҔOF$g>%dPRxJͻni}T(q%/ְ;%`(eNk@:SU()CE NyyeJ^Jf %f׸=OF{y}RE.% p8Q抗Ltl+D[*'"6ojq{^^Ě>jQ1y_>*T5#vu;vI"ewb/X7M뙝"R{yОcqf6 'ˡIIKgewj/pY`9OpC ']"-3,Z]T0OöZ,'IRtGVe{t/brtt-EӲ=h/2zvgZ"bvgםcZLR>-/rvt5iyP%.Pa9OaB]^`hrrt5i63---hk%oQ~U21=jrt1Q8=_LBL#؞^ i%/wӧe{z9BZN.e-k/]Ț2'] -aھI  |LIWI4;.<%BLR~.NR  Ych)}ϛLٝ"Z0dËÛcڼޙ61.# cMɺ3'amb|%v{|@L> kQRKݞ_w|e> [ g;@LV? kI<3bMIXzo dEz93ambv؟'f-3]mZ $;on򼑉iHtsA &OFtn̪q&aID1${@t4/GK3aN {FRM3#FÏifw/\/Y=58@˧?~?yCL_÷EޝpWH4dRHt?O?wf_j4&4!:T.;&dha9k/QKa{01[vg^k1,1[vw27|F }4݌` '1o/̆xR4L/}d}= (r-;'hѱ/b,ΈsldAi)E5w$4V>CN{ Xzd\{6m40w+?)R{ |?J"RI~p~O_3Ϗ_/%%O#mUS_|ۜ7?姿_߾?_ O?Wd.S?)3ͅer|~D_~2 |;? =g|~q$[H2_˿dtr0OLV ۴Ns|R<7سy=}؍=ƞvc~gnYx7Rs⠻gAسFnu7,{* +<[܍=Cwcb&1?#wcZ̻gXx"ڍ= ƐvcϪлal3f^h~<+{ގʼn=wc=gCݘ{ۍ%Az*ۍ=[ƞjvctgnL߹gcb]gߑسnGt7lո{ۍ%v6Ռ~7ુṉƛo$⫋n ߘU'o<+o^ՙwcϒt1`4*Hƞ wc`سn,YYx73 Sj8'-_}V؍:~o͵%s6vt^aNEv͑9x9};%w 9n>߳n AhY7y!jn 9٣f;vc&&Az61܍E~6Ӱ='ig=X|wvcϢ<۱W20=~8_1ggr,^vccmvcc^ϱ;/36&'Wa+I|>l C ElX1B1g~6l|N88 ?p6\ᅳc-?#gGz A868~DGFz?"lFI81q ߏ, y~2gc6^1|;g3Veg#u{?fe|l89&X>a? 2s;\?wφ1J1pN1dGtZ?8gg gr4Ygb~8l88my:~6\ᅳc?p6|!#g#XApߏ(8Ѱ#8qӳ1AwqYFc¹e!|;]JLGsr; =냙 1]țMoYDbg-k 617rZ˷cWލ=MvcG>=_/ wflj+LݘcGGϦڻ1άnb6Z@FL9\;3DF_ ﺉنg$n ]y|[2~3`nVa-zٴ|HyI0~Cg7Dv'gg׬1ZA&7͞L&xën15gٽr;,1Z՛r]W׷«v:&뙏ʼ16M`#_`yfL 1?]p܇b S >"Bnφ1點P !%a9!DA3=>F~bbL e+ oxv Ľr>&3q&d-* +_LOߡp=toچƸO$Ƹ>!1n28y3-`~~nc~:9}ųewzZ.{Ǭ[YaŬ;%:~?3*zyl 5;W۾/Y=XX`<'z GQω!0y_=|vc9-ܧ`9;R -g1@ ^^]wϡc'd='K\FOY~_&FJ?H21KԌ$7xY e9{x\[M4LN ʬ!_K!{cOcL'v 6-7X(Zd1sH?0ܔXy~sx>"% N1>[oNSbc|}\c>1c&>mtsGQ>>"ʘ010iN7e~/|<1c.:?^$^r|LтνA̢-?rXb<1s?k<9.<= ]cbcg8fwfFivy]zk\*1͌g>g19-lX?tcc=8#D>f:]kRkKL'vs&[[b=9ᄞ<^¼Mlcl3;,)?7w"r^X~^k{y-f<1pc1.#d|hڀx\$69|%x؍lΜ=0Zf~lBb<XAcq#y,w|9c,{Nks,͍*ڼq|c/g$w:0AXi69Ic1n ?Q8Ι3Y #^^{iW,f'9f=/qc*q;&-y˿8I 2WN̗Yo$.x˱>@s\wJbFf3^ GyYfWۿo&g pƛ׻3g]ps+%Ō2xc;F&ح<&[Vr痹9`}7B7]~co9nv2L'·fsdcGlϱ1lvfgE9w6r3Z ;!6`6n1f3 ,fcqdc,sfbxV"oX<\/^K1K|,nNgײ`n~Y.&cs,v093Ac,&5>_c3Oys8Km.3y] _@x?W1d9w'9}EK\M %nyI|X$z`yq1푟K1.͹?o]`{ 0F>&cey,n☂X@L˯o{y˂5יCn+r8~VoVs9sVE9׳1OPs sÜ;0vǜ!}p櫂y>ṙ>8,1ylcUlZs//MwY<~cqys%Ŏ ˧y>|J3ogrlw9GY<0]f;xlO}kRs~2ű<}|9!g1{b|ͬ7usn3[[8L 3'oU`Y8 0y.o֖ݓ >pi"lr:pNA. .c\<9ҏu#'= x8<}Ѱj{_VBFGޏE0얏|<&9Ce|20Y<|DGf=W1pE<!k N1kX,\ 0~?Yo;%z()ߍ<,9%yv''gzo/?\~mGsBlܯX<732!?12}& 9`XYb+CB.EJKȟ3o$aK>W=82Xz~#cd!5,6f~?f~~ybcz@ coU?Nlc/DŽuV* {>b&O$1Hcy>cޜs,7r#9ǿ-nICΕcv[cq#^ȋϓO+~ TbcLc?kn.sL;cco̗0x拜V)Ll];qPsLOL?/6Dp,?g <rGf^c <ˣs?%Ev\Yqcxy1y؜Y \o>sz1fc,Ol6rw{!126ݶ@'/"2+Xk~wmSb?zyxnڲ?ww^¦>~!f0r [];?YqT6sZ1זlP>lrJf^>: FC0툧:r2_V9t جYnݑ~iiĒ^|m&7;7[yJ{Ǹ sݾ~+;1J͜[rNmjI|r)}} .ۂ0dzFW_A(p=Saͱ) γ+*CG W0}E^`eT5y:f{OMM˔; >k4,=ܾ-¸*z4f%߿ekky;wThq:pv >@C[NpBpo~8ոp>F63g}'qU*},N()26ܴ{* cc6q}p@!*Bwl1\stgW;Ҕcm`p̤^7bs@4m 1>\÷?|Kl7șmyȳN9C&iu_޿H7czS:lgLFߨK_W{G%Nr'a 4e1#I[\4IqϏ\? dG|>n|j<Rqwca6.K~#fc=j-'H@mn4xYgR,<d`wzy֦w'}mgrؐҾfv01aǏ4gHyC>b0lQCSa(jNP3bH(X#B:5>.L[%0<[R n{S-{Y. ¤qvI;p/Rx˔,3 ܹKl{cپ﫶̵ }b}r7׍=[΅=C:ǁ}&gcR̙ >=}zkA7 [i[2|X>`N9f.}osY&q גZ|gc-yMf.hh?aُ0q LXbK-!{caWzcw9mwMIYR ;2/3IMbrx|78:m9>ְde\ cgF]k S#f0ܓ3(/`c:t3y:/,kly ǴyۅyW>~?֍]GdWs;ƴ}Oqp* 69 ^>7~:E(Ӈs㺶ﻴwץR2{)laO(wK;_;]ϡ}u{^] ޏEC`7-lc}j_p[ӡێo7;M0ߒM[Ø5W׀?ĶyRr}pMryŽ4,nj3}\;uчeLT[Nù3Jcy=zpȳ{4{- ~Z& .$v֦Y};qP|dsQC㲜{@ר701 3i3e 7wX'}U7}/-WL!mƿ|wV0xaC;eS=Șu{[8=y=1t Jr9>#ΨLe{"ƚz?Zv[?K Zǜ<|_yv&yƩ&\p*f,N}9 ߷CYύDQ80r Cw{oQ'`h&؅O70UY Gnb͑5+NC["Tar]JTluF0bag'ykMlhƤ64LvL?bHԇwr.È~t]18aBg]tlNoRwE#V蔱B[G=–}unx-OMiK8\,ٯ%Yƪ\b}ȁFv>ŷYu{_1w;d_[+9snr@hwbN1eyKR/_C[|a}9.帋+RQ|oWC·581!nx^G={ oa7c^cZ|YItO 11 Xys8AX^4[OWk;<vLX3o04r3ܫt%}qMꂝ0zwcUŰK*,%᮲08zV+l{rWM`lm"֌\[?OTׄ#Wh3< ~Vn$419<3ᙩ t= gΏofxyugEz϶]+9w$:卭̍orlmbwwn}[+Dn᷍kQ~*v-BKځdELډ"!!*]^zZ-yэoJ&dKXɩ^`+Crcﳍ´"C?s<(hm:bk?~;tO"ȻZ{qPŸ&i*7fF <1hN-})´}o榭nn?X'j\Wu=[ ^C\}8ZG؏gh /!5`%wCn9x 0tVR~!idnZaϫw42ym8{Hn.m/ |V=xGS@3y*ə=9܌`CqLϵ)_؝G`c§"/!P]Y}`\1p{}Ks}]_u7?sOO&rij}̙F>LH-"D>'$bK-&[>;粯nC!Yח2ZN?Wc-w ͕%o~DTosJ ~)nLQ|^uVz_J*j#_ }Ze+ywUϏ\,+IXrKJ"Z cIk1TIY}I}Tŵ̅;"\28C_X;{cĴ3(7ZRf%︵#r\ڙJ}[F%e#q-ri[UOqK[5}q$;և-'2+_!'k@; 侏J;2$2$h܁+je[1fl,c=+ZC"}dzc{NN.WfL;q8 k7u(:>Ջ)xeU"{hįξ_\w_ʡ[`'0ϟkvv~ؔМX}6?},QG7k9cw3`lbX-'[McˢY>>ϝܛ–`;\w|{)c:9񶽟_1ul=?>`crN׍9^B-®R|ͅ=2V#K,m^쥏vyVYa{ڬqmG[^x8q;vS׵l/,8|;r}/x|frvyzs\;؉q:U`Ub?r$"ղT_*~p<.l͵nۺk6I}W56%0]4O#ף0t CC+L>Ro}~ۄ$ Nr1<=nt C =z.ʺZ;ʌO&;:̣~N^*{sUJ^~ȹv08 㾶0 | wB#Gi?~n -ל؝7U9xv3^9j̅Рm=ƶq'z51yk?Z-N\Nk[5>>>q]?aڝa6svR\ _ꘞڜl͏eMk|<۝oksVkfLN)#E0b kuVN=-olҾ`c&-^u_MfD;gƾ0H>mj-0t) '/n_R}U+]}w~{\j]jaąy ~nhsZj#u'WgF`Y{~0ޗ}Љ.uxzw~?Ʈcofco;9uY0 Gc-"Qp-|wY2\i&/g0J6:cx'W#yyNn\sƘ_ʩya6g^Gb֜2}G28GM}qÏGnv<-:[/1}}Y̷Pv#[ra>ʡw0ic:|Zm]〘bܖz^u.˜aWaV }5gXs̯:鏏Q9uLm1vO# ץ}/Dڻ&~I?k+F`ޫ1k# _=3]k`DswՙUȅ|󎠎1qgGD?72Oks!]Yw 0jZK%^c>T_dY& a-߭sk6fs5iJc9 `:ePǀdG:j^L}y|m', f|XY-n'BԻz}ܵM]/MºwE#=m#RS^𡯎6\emY툥,0tt~g=dƠLpf[>| O!G9fy|r{-)h9g;1g\kCGb9utIBEeS[au po~~Z `LǮ;(<;ogw6I!1}sEiV;ɖEN{\#I}h&; tf0m$/wIY侾-AgbYj/{,YKG|K0e`D0cJçcQC.N {-L&Jgs9%˳sj98ͥzw>7wqY~82/"BI\<>c`58'㖾 ^;.#z^dq}+~Ȏf,mR ' c}1ϵ\>8%5;NJ.Npf^̹ᄎbq '7`\Gj:|2GO*;hFD5hRbf$y=BWv_]",r=Gemu0#?$cgke_%j8Ĝ[blr@^ۉy'bQDZq[xCˆ;d]w'41bufs\5QP1<_|}1,l;~m dpor.Š}|ܾe?gýsapٷjz/4B9?-GYɞž.Xw{8랈oOyW鳃9<Vep?z~<OMN'>ӍtkzGrĂՏm"kbamRK|}ya-wO-""6ێhx]jz1 cM{ 2c'ZkFPrj=.u`?P[sO^"GrapC;բ$: va¦|>~#n6^ ח0nN,08B c_- N #0t "t[B=gSx%.L?\{e*L~0 #O3a}OadI}\LMu ;g?ĭVc>I^Gaݱ6sa[?pgnu=x7ˆ9K,1hΒY %}ȗڶ68=t>KBap]<{csC&_gq{ٓx̳1h=Ɗ,<ۈKYNzϢY< YRr.حXW;t Wr\ 'W8Zq*>`q]BNqjk?\zݎWg8r\bNt~3Za[ _]xX%׭0zfxWCSap,eنlaY|}=!1>=SMi4E{dy?D #%*qq ؎qWr\渚q5JŽjݎW-6W8ڷjwW`O s]K7uxVdOhJs':ʦ]šm',f >r ӆ-o#]ӗ6Z-e֧\}߭,"F})>f-Xԛ/;:>gg.ISaڧӢgsY;wW elE},=5N\v}p{* 1GL[Oݻ\M?ևi]QebϬ }EWhH}nuO;]̳iף}LۻNزTu8U' FYX^[c̏yf/ji{`r;–G::u칝34-wGT\ =T4dztuJv aF^[rǦ'4t=w=4m{/뽏>u<;F}?o auCvE ]$ޜ !o-0{8x 05ʤfJR+2迤7crMZrK ԧ /Da;qo}K 7&WSAS(f_6L΅>SapQ׮}g/Ğk014{c!v'Rx 4hQ%0H}Oͅ R<}7wOGnG~IR FQةg9OQ؇,=3?˸5?w zCayυxw0 c)6RaD`nڈ(ǹ0|6lKc\r*ƥq[hncWjU e(w$U:" |Tw7<. ç5.CtSA6^2y٣Ÿ, .Zu3:–m)=FnPamX04, lXYΈ-%,̷PGI`ΣWGw;`:Gz?/Mza+ #Zv|hFxb9sCG\x>3F,x%yt4VĺtxFbQ;b-ll[Gz1C +4Իްu^cn|EP1AG9x [$7j&dmrvX[l!9,w.Ɲ6mc#VeK?4`P [Cᝦ޲wԶxvrĚJm& h$h&ciyޠ0*˿Vwzs9ćQc G%2b}0{ȇ3G@a Ϟ #qĶq# L]8qh}3rx[` 7cw|sPygTq;jb:`UG<%3e} 㞩H>"QaTG \ǼYwZc>]\E (1ǁ0|Z#1#xDB[0>ƃ8o8` =̳*ܧCu湢Kl /bZqc4 `۬,g7qybn1|sr_[qc}Q~H"mu|sOFb 0t >A0 C7w$=փkSroO[w }cdubbhqnc]yfZS{h}%Y$F0}kOeR0xp [rV-.8ik߿۾I7T*oxDz8 Ffx_{|q[]|7-]}}]lU{BhDu<|GK0'"-`zg\VSy|7Fʳ#˹Gy){]G6ul {#\eWeXX¸+Jv{_4'0ㅑ<-o̜DҽĄ?XGan]ਝ9 53ͳ;&\3rylXDZ}{W{)ˆeׁ7t^Eƚ8;(@"𞫅Mxh s( Nk猻7Q)}h߸lh~ ر&>x-[36HM}KǾJɢLw, ֍G>ڰy~DŽefJSnYcsoQ*=ć=XqIv)W-W`hpP1}7?.ֵH\ԫlw:jy_ 3L ~¸O}h+:rƶZodxh߾7qVԱ{.=hmsh2߶p ֍n@o_Pgsw Q;jSos\g.ha}?9= (iw ]?i,mZ3 ~+;c5wxk/Wђ`J? u{u08e\M:3F= υ~\/x,ڧFnap%-yވ 7U [ڬjyo;Dw59/ /,`38XƧ~$ؓS\G1^w`xc!}C0O4i&{ĘÿV}7Uskfg}cJdz viYZ\fNJb(E5Tbq:ws 5 >8kKH&m8 ]B0ݕ]l[wJe E-gVn>U]3ړ9#ķ}k+'|ܝs.u|?u<u}\ڄmxe˭a9+Nyf/a(#tr$j3G'`NCayx&/ٖ/Dz1ZE\d؏Άs[49?Z <_?p0|K+A|\nmc_-l[oZ|U+l|oo~\kǮF]ol՜\Uy^w &0rOlw\Q<"~qYu<7d}Uf)~[>S9.z-?;8َ/({, kh%L|C4-Ƿ-vr{wctcO>~-~sXMW-CnL5mx-\y|Da}rz/ /\B mXn1 [̅gMJ:Lyl#]7q+)˹rI9yYuh$?0 kSlX^k \0vMeʿPS 0׃~qkX> .kucIQ~,^B Ad]beM~M{Dփ&69 ՓI6i %!}ϭ>?eGmoub 8"X;Zy+9`{w|vĠ%= 0옓gE\Ukώl}<5gMV*gH[n~`}纂L^kx6n Ø5[I J9XwO=Ϻ> onh垘~x%Ag]Ǽ|v #k%:Zw\ݬOV+-DYb*Sntb:*`>vl~ָY^20ZS^1jKa$H=`ó!ŚҮ.m٣r)L,Xģ\BӚ1z}9~I)dNy0|H:[qؽ6>{M͑=ԁu[nkJ\ ;WC/@$k_p.I% l;eloae_f ~%Aa}&?I\{ibͮ倱 }w\q/yxWy_KʔxYkpUV|5NXIܰ_&q.wWEfao 0]"Wo~m{'Wزz/n 'o60썹}sϏg'.](eY0s= a,t+w唋9 eޡ 3/Rom[`KGkطWW;s#ιW}`%}quؿ`3Mr_( N8e9rkU6aua~j*藎*20Ч{Lbw" QoE7NǴ8;P`ye`_[.G98 s2~qH6ݢ°vq}_Ut}(܇^6xЛ( ۻL=w8y1" m)wG_0\ +|¸,Ӗϝxo`Gq0o#˘^ot, NH" Ιþ_Gu10;=r `5^Ȯ<_gn<(9/Ie޷L/ ]rcXn=#gt?|F~~>ﳝᄦ->\?r<k!Fk0Jvo6NF=p`'RcZl_")@HK;۸|6`Bƾ|l&EOsT2߻!Mp.[||2bN,qxQK-us/HudobpA۱?3폜X7"l9# ;oljPl 71)7_0v7VhGn&5h14?кtaq;1u6QoǗ:z1x\{?aYQ"mpҾ?T #F:Σ!''XPySp V6Ƀ,=%P 3|}N|xIdoLxW7[S锑]#ycQG0{ 'i19/ķ֋M4(rӅ.6#^~g~GxhHap$yj$QGmr"4Lr6fIzy_a~@93%o{L~M>Xt'8qw߾R]2jXj WA@Flqk߹>L_1xüh?nB:Nb==2kٹs)>˒;:˲kVXz}!w;g/mc>bw ~vjpmYxIvw Z2=t ؈,1rk]8܀[oUo9ɵ"[=q*E]X'E}Nl m>0¤t."FqavX}ctn #.0bbq_G!y<_Vֱ}OFqaޥF584/ꅅ++_?oƟ_??S|?ORgKWmSv} ȟ????yu/k^/ۿto_Oo'aprx-2.08.svn593/debian/aprx/usr/share/doc/aprx/LICENSE0000644000175000017500000000275712305736167021335 0ustar colincolinCopyright (c) 2007-2014, Matti Aarnio All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Matti Aarnio nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. aprx-2.08.svn593/debian/aprx/usr/share/doc/aprx/aprx.conf.gz0000644000175000017500000001237712313357163022561 0ustar colincolin[{s60Hɼ$Y[uZ[IٹhĄ %M߯cewv*eghnoom4gF9Ŷuj⧩-U53R};Wk]|K2|+q,u+gWu\եIMZ\8Njw/etn-Jr-\er5ץ rC\G'ڙZZUVU!KKwB]:s\l.T/!Mf KeIWQv (NulԎ4Hu/)Sv)s:-^)&F6U,`) > Rj =9V5лX/4- qZ"zjLL)U꩞.u}NjJKw}ړK>c縉51**ŋidZ&^mmqa`P(2G㗪v/M$l~H :d^{=4k%j#O׌ Wg"Qr?Ȼk fi//_j6"MSU UE$8^t &՘)>\aliPs[VEOLTg''K 5%+md%@/{='kEfcSzlVC*6%lZEՅrMX,5aGffKv*=ekq?\ly.SZȓ L5Svv޸PXo\pw-@ V^˱WHLq1q^  NbΠCuJ8^cɋj(֞ZgHhɃePE{>ygjhxJ^=ϭl[Iyv9~R_MI+KM5IT:+T/TDp+Pm=Ρ%fj[ڇ+/N^–Y'滶 (U=z0P=;ƞUB!uX61H>|L>+c[{K`A-,*0ŞJ{!5){(E>>?3 X& PO߿(Ӄkg/"N8ɢj3X!RKlB9zɫh ûA {EJ?`(ñ]FX*v #_yo.$Ҙnnsuˉ[!X"~Yi[ԓ1.^޴?!BH@l2"i :ӛxMz.e`vB IjOrtzZ M- D 9f)ξ'7+} B%ϵhQŝ [GMPܮa(i=a{1iTSw[YCUuB̶Iv2-6Cn}'0m)?eS4س WгM,f/LeSKMko܈kw5oMU7_a-_\2.L<[8.᢯axHl}9DS{2> |޻xa21*#Pg jma"טgdq M$` d/bN v$sc9bXVWˈ OZ'&Nʳ,̙*WgG?1y8e'7kVD,͂Dre ;}Cz4ܻwqv`{펧)kם`2( e[73-?I)a} Hhov=73) o9knAW^+B fL^px'LwIDžtv":%RxNDs4# <&=kh@ZDz_fݖiǥv2'Z*)~?fK;mi+[$@;8=9f@.ixƂ&civ{!.✳ %\p>}t?G"oZ 4np`tK0}:CجNMװ1[H$S`V!M APRS/APRSIS Rx-iGate. Can even do D-PRS -> APRS RF conversion. This is experimental quality for "GPS" packets, the "GPS-A" is OK. - Does not require machine to have AX.25 protocol support internally! - On Linux machine with kernel internal AX.25 protocol support, does listen on internal AX.25 network in promiscuous form, and requires to be running as root to do that. Does not fail to start in case the port fails to open (running as non-root.) - Built-in "erlang-monitor" actually counts bytes per time interval (1 min, 10 min, and 20 min) on each receiving interface, including all that feed internal AX.25 network. - Telemetry reported erlang data is sent out every 20 minutes, and contains summarized data from 10 minute round-robin memory arrays. These are _not_ sent at exact 10 minutes of wall-clock, but exact 20 minutes from previous telemetry reporting, and first one is sent 20 minutes after program start. - Telemetry reported erlang data can be sent also over APRS radio port, but only for ports with valid AX.25 callsigns. See aprx-manual.pdf - The netbeacons are distributed timewise more evenly around the interval, and even the interval length is varied at random in between 20 and 30 minutes. Number of netbeacons are unlimited, but their minimum transmit interval is 3 seconds making the amount of beacons sendable in 20 minutes to be: 20*60/3 = 400. All will be sent, but the cycle will just take longer. - Source code is at SVN repository: http://repo.ham.fi/svn/aprx - A Wiki page of this package: http://wiki.ham.fi/Aprx.en - A google-group for Aprx: http://groups.google.com/group/aprx-software - This program is also compilable as "EMBEDDED" target without any statistics buffer required at the system disk (or RAM-disk). See the INSTALL file. by Matti Aarnio - OH2MQK - oh2mqk-at-sral-fi - 2007-2014 aprx-2.08.svn593/debian/aprx/usr/share/doc/aprx/PROTOCOLS.gz0000644000175000017500000000670112005774517022245 0ustar colincolinYs">ńT ˮ #Llհ;XάIgfw(UΏׯ_VuWd6gO*-*W^gFڭ?o}9-N*L7*}//8]j:݈g-bX.K6;0k^qHLD'IֺjS,y-u`Q5Zskm~^?VFkfAZ 3sUX vhKNLs!-zV:V˞U!'wBU/ WN1: !,y/r:' ODV<$k!+v>-Z-A6+lO ) ̑%&[y};#z*?Mqb6nYg7EЮ,3vn.Y_?FH&ћTISeUDg\ή;J(l0/*Ik@vzhTT(+dv'9̞@H!#KGEhtw2m{]NuLnP/-Eu-Z{-^D"^MgQI``X#f(URd_3E~Y<^9x"2c7ȕ4o֮b{ ["4+M -{~鰆hԧkZƔQe$~%N$ P]Gb'C1X̘˝B.s>4b2 6~F<&0pl'V*fTH~`Nȇb2.g\Lg^ ~o5āƍ?QĆ=̔q\HŠL:eRRB`Yȉ4_qb2vE~:هt|?+mi 3]Mk%mY>߀EEJWxTY_oG5*pX 2,.ݑ*v(AC|5ci[g jO*%e nEI KE# Zؚ+[ǒJKlVDOʎB2\R"eZC)D-P t[)pG,T`q _hhE_C']MYHecȣSr(`r}nIf>"K)# bu)'|PqKiO l'{Ը."6#=&d[?xYfd Oy{1(QHB<\U%Ӗm9xqz/#6Pt $mW3O}qb0~#8CδF," T8B2eV, Ϡ&INMJ%Af|FF;1ѧn猼CXYˍ g!_fj׽jcĨ=hM^77_":pÀS020|.o"d~%316fiC*u: ƩEm*o!8ej}kr"ɜDqH7mZ\=d?U'"eW'u|ӳp'ޠI|Q-6 # LήUguMUԗg5@Eqv|QS! 6yVmY'raB/újS6Po)K ԹةdnjzNʷbyQ[˿:d؂k*x+7觴ۥziMnEWȬw1~a&tk*FX&k:.Gۏ'e B"CȓsWVȍFQN~8Uf{ nJ"xw!:,UĬl:S@/L IN'V4 o 8ԋ 'X\#(aprx-2.08.svn593/debian/aprx/usr/share/doc/aprx/copyright0000644000175000017500000000001512005774513022236 0ustar colincolinSee LICENSE. aprx-2.08.svn593/debian/aprx/usr/share/doc/aprx/ROADMAP0000644000175000017500000000122012016465527021314 0ustar colincolin Aprx Roadmap and Future Directions Version 1 - APRS Rx-only iGate - Complete, working - channel activity monitoring and telemetry - Complete, working Version 2 - Digipeater - Working - Analyze and detect station distance - Working - Radio beacons - Working - Bidirection (Rx/Tx) APRS iGate - Working - DPRS->APRS GW - Working Version 2+ - Port to ucLinux - Planned (pthread OK) - Port to Windows - Automated coverage statistics analyzer, and reporting it via digi node identity beacons. "ALOHA circles" - Automated coverage plotting aprx-2.08.svn593/debian/aprx/usr/share/doc/aprx/TODO.gz0000644000175000017500000000421212313325035021406 0ustar colincolin}Wo8+u{wqC@K5%$ )9-Z86Eg޼y3z=.nd|~ ^B+t+O~vy5I&7ӻ+J[JUoϜRYze*璕2TkUK֬J塚fLcSHi*Ӵjҍ*; Gߥ5#*-h\=ɌpލryM$yC^ EdٸQhkE]#LxpLa)0T7c?p{|!۫FwĮ܌HtL|7udn8zxa]bqh6b7*]p?1[+W*21sb-)TO_/u%I7n5'-f12Y0}>`6 ?rT8=`]K7lr#TyVlGA6֋ʓwȯmQu%܁Q~l;pjXI7 C j@cˉ,L W\R! 4praGF3/Ab.[ѐS@whJ@3vN;D#eQ_qXX:ыoP\$' }ܚD{S>q~!E!QAZr(65 Q4&M,ml8p(V hՈTNQ( mAɞe,ģ t`dNȧTE=)i"X;XCHCXl~s V2c8 u!m6%ԏ yIHTk>PKbeY *3T%7I /XdgHOBZVw rM4lqtrJDpraBW< j*V\E02\6.6V}g5Y5kxϱs= XYobHcJ qq<I `ƚf wGց<Ɔޟ==PSe. Jj~8Hø}}{n)MlV ,uw2a?0v&jGD¢'~wbR~$Ӱ:&GG'R|Raprx-2.08.svn593/debian/aprx/usr/share/doc/aprx/aprx-complex.conf.gz0000644000175000017500000001553312313357163024223 0ustar colincolin[SGY+Jͮlyu$d@JFOw K?fˬAn73]UYά썇Ɯ"&*Yrєa%x4+JSϭ9x{qkphœMCX[ͫ"YX*n`w󤺵TuDuS\', W\b:=O9w͹J&ijEXVH6{egInndViq(c[fg˘EY%s_۲0ua0_ʤ]NJ ?O-ʜǜlF%LɚN@G2 Omܿ'68D|IqK'Ͱ6YQDU؇]PHCZL#qF,k0M"71隐y*:io~ofVVx0[ʘ˃/dKF |"I@&Hq<Ln,0ĬFCô*y\[v"@A5w&@$([bqe,;mbD3^EU%tٲWTTmS&,Er8mM(gEóbJ 9 ņ'0ɹ9;KoԪXP;eT,jHzB.hџCH54e`yJy~~;BҦj$J?# ãs[ߩ8<0 AT(TLކUgY4e~Po(cr"Xۢ(ɦT}0yQhت,22ylh䩭*b&\ U6Pҥl)Ca^䝓j6%ˈ,AA)}8VQg3HR&4S;[\O5E6Tiá%T(k SUIҤ^:VylL^Lӆ"u~7PA)e*>.mM-+040BĒ#6^"L (|ʑ>ÍT B*z`T,gjdx~HK`,=-8>M.L,{ X0AXyon/HҺHb 'GA%./\:ƴEY\eЙ`I쌴xLCC/fP*,Xhߌ1\N`bj$@rzX'Y4L͛92RpD8 5VBN_`VCi SJ"EŅZ*+?eOL|kO($7h7(rɅ;#coъ֛J*L컴!ؿa^1\rBQo(C՗ }G'[=;25p,ͭ}!"3 (m_y&hAaȻN°ܦn;[ah+ "8 WENL6)Eˣ@!$u'u 0rӥMAvZu%ͭm?B }srzrmO뫃7'd=~V=/osЍ=>EB>?}y-lrEaمm28 ]!ȢTĔYf1X1kMt5sˤ1M(1w@UIvHNhѫcABҸn"& 4#X8BڙdO'hk ׸ ǘ7>\)RuK)}$,*c02G{.!.sRbd1ye%Xy?5 }(pl^ uVF6S!.^ٜBp=y:UL-"𲡹$4mBf^\9/D 3PeWYR8$LdDwjZ#XH^Ԫ,9;<>iѫwg/eupiDVr "lk d=l}q >))&n ۮ kvh8ڸ_#dң>L&F5ldr[{>,)-gq>wV@4aT4sNBrYKĽxt~>xzv?r7>Ye^Wnw'/woaF)[#AI %6>n4 ۜ<”i|;ze&wVm2KJѾU3"ث$tg3n$'_&Ȃ-c}s/ hZk~1etBtߏt[ݹQz#by>~1~]\w6 ە]ԫzN홗 .YǝMEJSp,$T\@F-x_ )V3V(Aapi`VV.Ǔwج=}OO5@:BO8L%sU{"Fj^73f1^?^:,m\}\ZW ǽ`%P`V.f!zR#He Y\?Ꭵުk*q˲6%hZP,9e ֘G!RNBXcC άTI'sӦZںs#z ?u _FZRAZEt7Kur ۋ։uFloX2pbpLIy,/6Jf//?: ]˺:.sEg-\С1ʒux to0ޖDxt=$\q>EFWeemVԼ4c6]",O/(«Kt4KlW,ֳeϝH+@3b6=c\uRvip&꟤4-!Fq5>i p}Eߋt8 ]3sh42@*+Fwh~Ftu)1K[7euL'C6!C_%GV#tmm윷6ge87~Mv6]?sKNO0ɪQ䙴WmoL0)J>Ԫ}j %9ﰖb­+<χDЛw@usHKP {eբ_^ 0Y^H4cݬy[& ̙2E'n S.ũ7dF>)<*DR~cIVeWo;PWr zrI eIK{vp:)UƤA:Z5[kY@fO}}a =dz>Ҷt-NٺԄ] , +Fó? y$R"SifF-`"nFm}n4Ke'X m'3<#́h1R 2rAW_*Z#Jth[̶iR."_XȲ'Zjҧ>"53 fpF$6]Q6/Y.:[otڨ]̕O_Z'Sgm_ʛqQ%Yl?n펾x^Fܮ䬼@p6uUo~^ :LQ,%THJ(\5G H}]ѕ k" Օ|+ŋrBQvvaJo4'Ed,rH ߿tRT ̷ K VJquV^JVNZH!=Í?h H!rgߊn*/Yϥ7אW-S%jd&{p50hҐ d27r"'!oudW3%\.so k"z9iBY ad-oU^h^1?E#C0 tGVEW"d1r꺀dlxvSq÷)п/'q=ܯ0侾p:ؼӿj\5$Ik?f"_Ym>{]"GS(߉zڎFGݛRGZ\MհY7evU{OwҳT6]hx<еH⸓FJw-[{w)5۹޴a( 7)]1Ti `PXi%\?s&$*M^*s hO)%ݪ{E.%QM܀|}AGi#GM` H@dccFz͵LCYeZp m.W1ˣbpb,olo@TE̱œ Nxڦż)ؖ}2T-T3m0JiW G11: 6ɯt[d佔 D3@ Z6Ӫ,%\e H`u4# c%fUKh}.1t]`1<z/=`v 66jץyJl1n$&R~i*>J1|xb$ZD kH%Rd46*' ˘Ԣ"OܚkYAX,! 0N ݗoJFN-Nb3'9Kog qL㐢}߀oޚ0O YKaRJnн*2^ṮS3`p?}DX.yx8j iƃL\ViЅsh!AQ>2%Kw}.OxJ3iEr6yη~0dU7U当 7Jh`aF)w:̉RZpᏵ#̭\ ؑ&[kj?@dqЊڰ+G{jܥt^h,O ,:-T32:; zfS8tF9%͈?'xF_q\Wb۽板ѼU7ޝzË2FgOy ,mw~1쏰V>d3N"$駤g!sAm3&`fMe[Bu/g͋6a 5+O.;k{Bg6K I3%*/ByQSnhZ Y4xyx.,/4hyI%8Ѿ߫ptOrf< n$X-a]YB#a$-' k6y;-s~pfF~.55IIH)ӎY `wskO+L:W3門Gvyo4IOn4!=W-`Ln=3#Pj 2 u.s Iv6(?|IM@DIWɫZ-I/s Kڹ2J]E˧O4QdtS2Mݜ`#->K"ԍIlR-vןy9b2dt[>\YZƕ DFwBtM0m:o=чYr%v;6Ufe+/\c)Ub@f,CM+ y锩=R\)X,&[JX1e=:hϫ[< XxacW,5E}h[=m'i_jB4 *+$<۬?Bs,K&c-Ri(+nFԞ6 $|JE8O~ 1!.s:JIIV7Iƻ'A/,&@@#{Fe@ _${eb8|{zrs˜??B?2_D8.h4Kq~dJI$N:95n؇`zw1q;:nOXa/@Bfq.>p@mn}pYp7n \'c,*2tׄKbbPz@X|ZMHݶZ8'~i93Y.!fH*%ބf s;QOj1˅t·*L(ܫpeт%mFc[1{w'ImO-Wyo(r݀g@A-#:J1˪r/ODzp(Gob#i wqOfI 2N4bWpW'6X}3\iV!^l[9=$̗qkw^VgOq" \$!N٤vwg0j$YL|i 0nKMT;Q,-Ҝ1!= ~2s82XWWKPӻ108_|k߲ SFiBM]Cpz**atwzFdvw <"SR a<NX!(knVBY&fg4).KR}ٔVeeR9&}TњRB <+CeO(bGѭN~* ω\Ls4$b"ɬLn"Q.i)#-*y ;3w0MO_*g'@:Z!& v$1@"`ݲ`Zjb^WDDD4&tŧ0I)*aiMg,Y+_?Ec, /&oߠHE-]yo_-w)[՘lG 9lCeg¯BkaLwDvL[tCV!Mdu{`ˬSLep+T˚vDHq,Żwwf\Is19~AIsЎ]Q袃=' &E˒Nqϒq8ycP{Wk ܖ'sjqg:t0K+HdB]Xs ԸOCic ѲʉmFC݅p8E >xwM| qf+HKYQdFw᱁>҄fra6*@PkeU"[L`DUMt1C y\X݈Fxj[FIUU&Ws/28S~  0q8dCCJurp&21>Y,JsC:KZAP 1@<@j/AIIScTwA@ΰ?VlVU[j ?p7k^A߭FE7{ؖ!4$sK| Og-{> r~۬Xw!$db; 9;x3H2C(ITA t8QR&J1M,kєH8-ON9ʦ.L$sGdmKԱsrhD5ɭDXUK1q 8К0[ߧH)B scF`ݭvGn J9|hqK()4]{(blJ=bv!}yD<t(RA$W1S |pu'P.W/Ԙ=,ڴ=z4x̠`O`E[)km߄߯-'Q[:imBEW\Wf8[&L|ؕ00yUTuwvlB5*`8gy}ò ;[|Gy3+ȱosR.߈eÄۼ@Is\nh#dH#^Q-߯6CDz @PvmYy70VG5NSpd ǁݺ(4oGfls&Kw'oyZb Zᮔ%,%mG,dȊh cAPo^gnM>B곸ڋJ$@LE.kaEr&2}}iJR7#ΘU[[֠]R;H@rF?E^!j Ɉ|f;x^7hlM6ɲȿ&*m׶pIc+JtDcmk.C4pS9IܯV>ZtsOd8 3LDLjۼ*WB5 i:i@] m`aĊh5hdž F6Z R!Įv>aȷܭ;Z\IGF5T FBL [9JA#(kc4`D["q;#LT/q|u,q!W0;q6ldT98.`2Pg bz#ב>xIb:~]!3; 79A!I{~ }H*RBfﺖ3Bi[O;i|YrzqX[^:(a4"hh mqõO o-<,؞ǿ(Sx9DPD^ 8۶SȀ́@ɐ<RB=vL:1:cDuSN7l0bwh)00[xǐ Xln1PWWؔwr^6mmݕ0~!d,3>LbTPWn'vLD ƌ?( v_nVdt<!Tb-Z1nY8RxH9:pp.M 8D{'3'ERxan0ĝWD%f@ztgVow1M$n/(X34BQƛP 6D _lk܉ֈu,~ia+rJ4JkMw`F43N%y=] hd?Fbm>DG̴ۼ9ƱYoYx[ rs~۾nyNo]/_y߼~h`Ry(6qMH`MeѝgT wbF7C")7$3G 1ϲ>w"'(,_xndS=;[+[w;ͺW1VZM- ?̺v_]Z9BضogaYDb5܆v8:e`sA z~TXô6*gla Ʉ x6xJ'*VG~bi vJl΂Ƚ_5#RrE?:׶T)'-”Ꝍ~1Ck UR'l[J }Ц p.ȼڨulowsfh89[]#o?}_߾ƿ4! ::@sjWj:4ͮgq?Çp~ҥ/Ob:U!ѐ,;^w#JKep#)7[bge+p%[8x+Iۤ޽qVĤK_= 5M`<^݄?#48;DX[bծr5MB׽${:dy@,Z1ED1J1A1+kX21IX$-&\ Zy.%LS0RڀRCepޱ7+mCA'RxɂD4T{!VR~Vӿ;l [z1fyΝ.ݥO!ަEi糇6J8]ϗj˥!XMk*^Kdې6TpXc;uq[}Ho`Aia֭?fٖγu 봻s-kX \EWJeڜ ?∃W5C[m-l kAR.bVD`W{kϽj{4|_(NX!u,&`Xp#D6Q i M2fؒy `#礄5N%]asCO96  oWjT7+ۿ5Fۨ AܤrLnI1{H0\ab@iAԱ`:/ 1*5`qPYјö^6kFrL^XӔupҏx\<i%.e>XTK` '`wԜ6.F̳zL7SgKHr/rveNFBٸyNt%q(NbbH,h6Q Y~\kaxG<#|ѽ&ꮪ^Gl|fE~]jz ֎ {/O})U&:w]Y*u]CE,)XPMgcQ֏ 3w\o|9/HU Mк-KNSF Ri1kof! aWCfm:j =ڞAߴ[B'w܀Ć$"^ǬN}1e"LXYRQoF76/+two/u(| Ca}D!a#ntV(b}a,!Q1 뚤,*$$1:/q>Kf-xѬ~52k]7!0w[t$yH,H. 5/ʗQ%6iS""J_!u$G~KȻsb9C9 ׵"&RvYdϮI;VNr`RYNe 9r̶C~Yn@÷][R faAl!t)Z~ a՜y\yK^hD꒕ fvTBҲ- +O$ ͙+̾Cیmh*Z䚀~zotuG᲏NU_(kqV@ZG2wL -ƚ챥ԛ =Y\>͗hO-Xwh5gÃA#0lޢQ@2o 'H x& ү6X+l2̯Zhr" 42ő8(W&K,YI:=7f&9%h"-5 iD/vy -Xq#ꥱWYW;WJWKBrm dr/xet E /4cYyRy":k\ ;ɮ 笵ZΘkf>6B }6[fw^yܘDMVX-Yj'p^_E$y'avm9{ď?v(OͲr+NG% XZh jDشY#]y< 4N;z@TI͊f\%lPJ$j"UTC-+E Rj붊YB߼ȥAWRs,~%4nS!K13-x{;޸(IX5$ޮjm<..o;vӯol!DBy,. ~KK;1]3g*iP@#>7M&P{cyPqBaDٺ]hGbEr5:$ q_<oߟhȣ bq`S|>% ˧Z6wзɈUappjNovvՃ nUEkG*锕JE9k=߫+LGͭc sMJe[ 6lB^ FhQjQQ:d+o 9S洒O$(C';c)C5iÃdдl)C >v!b*) z\2Z,N䠝aZz nW?S׀ߑc\AR1w,-TT&-Kqela@)Ъ:HBԘto,k *pFS+mKD<,(`@Ar!*?2Rý`練NW0|W"4El]4T>"5-}4Sׇ&`R1o8B\}R"_~pJ SgAhZ/$Թ=Lejԉ9փeY7ui[jFU8Em1EЛZ3-?6!>p^K(hgwޝ5 |>M^C>-/lU ݛ<:]*L+y\YG+<|yٵ~?q 7~/ϲl3f ;Ïw}꜔QFpnp֧oftz1a3]˞eտ=]pyb},Wh a%M=R*V!b̺fң<:w.U<$ "WWPRtV9[8{~}PaO5Id~2lEyiqa4ךl%:@]Q>z GBMbݔq#q$HhޫilA*߆j#DWÊc=$T(o*wƊQZi[U~29{- ?n+ h-nWh"646=gE'T.%?VXoggGB>5_-7FETi٠oyNc4G{q@M$LVܶ ѧ4z }Chd;# ŸPȈB+;6BëGBp0֪T!p6gO)>;0}QfW9i|2&Yͱ~f0zFi1#*Ve36NU^%ԔdDdbUם-\,d$5"@0̤ 5}auR=ȧ NϬM j\g>s0J__ɠZiގH+'G!^qY$/{J˧lMe_&e=uznSWv}kG]}OYƺ`omSr0 {P.`o&q,whVe'$ݷ{iִ7@'~ #-UK4N;g Q'"S4ҁb@#EcM2=k@2`"Vұn3Y4P[*ǹ@ZAQ`s%"ƟٍhP>d${Xfg{˹Ȣ"v_ґ¦_X|'yUpU6|<5j~ʰl ]a)]z]]-m:[vin5j*d!$ʵN@ \NoWB}o ߶Gg?N-#I8E'>==.Ѿo^Nkjl \È oĚ*SKqXSh~?~%!l6Rkrf8<:iY{`X jk|[VR7?dW'Ʒxdس&lH7[G:kN's Wpק%F5̓HQ|YKGm ))츜q!=5`'ƮZoϥ62%5ԛFލv35\%R(m}V2c~o-3ܖ>1g2AF>DHeI[UdzJyzˬUW/A2J+KJuFjR+]K"$,Xdf,dڪ8)PM}qYGi֖u+l]*Z4KAuwbl[V{d._N}uk+ 6TYXeDgŜ2fQ[ް(RM#Ө_n yL/7\kfmnPc eim $ףIr>aIC{IKsFK&<>8FE]HvKvsͫ_dQպlwuw,ZхHD릘[ miYPl_HBĦL5ZMՊ֞{0nzyt \r$]狚`Uq' φ[yqV"'8J2-Mzy X=L\ /& 5bWȳ[bMT/͟BA,k `3k 굘Ag퓾xaUDtIy=3dzUˎZ@mւ 6bb3^9l258@tYqRmˎ3'PfZ>)IX{U4 QЈI[KP":L˙#S!=\mn Pya?<F"ZI#u|>MG-[}}#쉗jy僪|Pd\.+t7Z=D:HKp-$I7e[UrR^;&[E.E=?<&+cN6OKzh{]G18*Cmetoejի겪D\*j`[_sߨ? +2D={eFuj;X!N#ĮxXg(c1v?viQ%FѐGCo~1\wMdkTsHѬX$JLY#hNs& A\<{ʺ1;] g^sb_]ȱQիmsÂ4C 7I"r 'i /d_Er = H587>=Ѩ>?67;H?8o|#?>/F_+eeOXB2a?>JaǗ2#)w/|%-\l]1unk~> yX_g'Jt)baf\>d[DZ!YE/Jk%kkلŜؘC*j-9v)>kY\wa:X T]Sgp!ceI)m^QZbU*ly%"Jd9'R:e Va"@g3>d1xm^u1ŜuQu IEd t*6/"9Qlk\6u>1x1:WOL~rzw2꾼ޙrNdU.yco "Y!!*[L"u~+wS`vU㔀G/ּ1-9Zk3x$I᭮cåFRr'M(}63C󈠶 [׆GMbC&-8BKs-e\$β鵵ʍlrG+u_B2y{R_DcwV*팉RWޔsaoZlPwYG]⃬4J]].ETpjzp ωryUFnF@Wl-IsǻwA A5m5 P1-ZojeX_mHɗ%-ȿI:\8%~VఏҮOL_h'[3.¶<[/Z+#DBc{Y,,u#n^c<U~A:Paso kk ;wbw]'|ݘx#7ٕ\pNWfɩ jSp4lHX(a[AVTm>Hys2_<;dgIv?Jkj;xVת fpf; SMyYK~ηrhHlHظy`S˴Ҿfm慸$ʞV>V$%݂%Qa&~A#gtG .Ht&3D&g~Fˢ=}Wv/_獯IKnG7,w;>З$]O:MD {wr>,\O|6Ȱi8x )sx SDsTc'I$3sn۴I7?}/RQFaprx-2.08.svn593/debian/aprx/usr/share/man/man8/aprx-stat.8.gz0000644000175000017500000000471312313357164022647 0ustar colincolinXmsH_˧ doߜ8\\ `Βx# l*M===O?63رN8Pvԡ~wB_ﷂvL ~ X&RMx_~6m,2ueˆ, ±5 fKp),^|AөL^9s{{^aZ09霞k+)b6­ g)N4]faCLޗe;4 DB I"-m.w[>܍1[6:)b -LdMSIN~w_&\SVp+^2%FE"3h'Pϒ4$xr8B""@?mFn^P{揩]޴G WnJDYlOG  >i(-%܃p.]W4@g^$4~??FZKR!p@ _vi[ZGlYiʁHs\} ѓ#~M * 2rO ̓1XBG|>raGDO^w0׮ǕFG.N0QAxr_6l|Yk760+}h (PcCvxVg &2WL'?(Qw [43"B瘝wk#5h6^d҄Ѹ#TyR9A>|+Ą䯞Bl_%zxCa>ʤMG67btXE$Ȋ;z \wF$pPC P q$Gsa-}jRfwvv~ &LAZ~{;%"~OM/p~og^A_aܿu{oi:ӿi75މ_751?Y!ɶl!co{XS7`;ך<<}æ~999`SNcvt]ձx~_4ot2mȳ:oȕCTobچMJYsF\[7XȨ9\V4nچjTv{D4;UdV7f[JM> }s^fÝd9ߴ}U{ݡqYH.s3ZJG[C3ҮX DM-OtIǪ>!fuHGi[I’tQϴ O_*n TY[?0bVo-/nڸUZ@Tz?|xpHW}}_~مkd׃b LGr xহJ Tf{Bm#%\E+ ^*apIb`E1̿oհ*E6OeM|*K7?=Ag$F08a7";v\T ҝ 6}]d< 5m.^)>@cv- ~a*fyn$U1RK,ed7oZJ؍d}NAsD䜟Pgut>_Tj;B7Tic[PtXڀ#Ս Z Kc*ſ6xRebPh .G~ŀJ\׿iq7X'P#K8=Kn2 By1r9V6*|A ~N9zM09?#9wyW)N>;tF~{F{BpyLwѫ[&f@[P5ˋ<'R)!R > !LQM,{=LoeT?|iQ|' ~ֻZ _mA-=_Ti[$`̽^maprx-2.08.svn593/debian/aprx/etc/0000755000175000017500000000000012313357164015431 5ustar colincolinaprx-2.08.svn593/debian/aprx/etc/apparmor.d/0000755000175000017500000000000012313357164017474 5ustar colincolinaprx-2.08.svn593/debian/aprx/etc/apparmor.d/sbin.aprx0000644000175000017500000000045012313357164021322 0ustar colincolin#include /sbin/aprx { #include #include capability setgid, capability setuid, capability sys_chroot, /etc/aprx.conf r, owner /var/run/aprx.pid rwk, owner /var/run/aprx.state rwk, owner /var/log/aprx/* rwk, } aprx-2.08.svn593/debian/aprx/etc/default/0000755000175000017500000000000012313357164017055 5ustar colincolinaprx-2.08.svn593/debian/aprx/etc/default/aprx0000644000175000017500000000027712005774513017757 0ustar colincolin# # STARTAPRX: start aprx on boot. Should be set to "yes" once you have # configured aprx. # STARTAPRX="no" # # Additional options that are passed to the Daemon. # DAEMON_OPTS="" aprx-2.08.svn593/debian/aprx/etc/init.d/0000755000175000017500000000000012313357164016616 5ustar colincolinaprx-2.08.svn593/debian/aprx/etc/init.d/aprx0000755000175000017500000001133612032354720017513 0ustar colincolin#! /bin/sh ### BEGIN INIT INFO # Provides: aprx # Required-Start: $syslog $local_fs # Required-Stop: $syslog $local_fs # Should-Start: ax25ifs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: start and stop aprx # Description: Monitor and gateway radio amateur APRS radio network datagrams ### END INIT INFO # Do NOT "set -e" # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="aprx igate" NAME=aprx DAEMON=/usr/sbin/$NAME DAEMON_ARGS="" PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/$NAME ] && . /etc/default/$NAME # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.2-14) to ensure that this file is present # and status_of_proc is working. . /lib/lsb/init-functions # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 # Add code here, if necessary, that waits for the process to be ready # to handle requests from services started subsequently which depend # on this one. As a last resort, sleep for some time. } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON [ "$?" = 2 ] && return 2 # Many daemons don't delete their pidfiles when they exit. rm -f $PIDFILE return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { # # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME return 0 } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; #reload|force-reload) # # If do_reload() is not implemented then leave this commented out # and leave 'force-reload' as an alias for 'restart'. # #log_daemon_msg "Reloading $DESC" "$NAME" #do_reload #log_end_msg $? #;; restart|force-reload) # # If the "reload" option is implemented then remove the # 'force-reload' alias # log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 exit 3 ;; esac : aprx-2.08.svn593/debian/aprx/etc/logrotate.d/0000755000175000017500000000000012313357164017653 5ustar colincolinaprx-2.08.svn593/debian/aprx/etc/logrotate.d/aprx0000644000175000017500000000025712313357164020554 0ustar colincolin/var/log/aprx/aprx-rf.log /var/log/aprx/aprx.log /var/log/aprx/dprs.log /var/log/aprx/erlang.log { weekly rotate 4 compress missingok notifempty create 644 root adm } aprx-2.08.svn593/debian/aprx.postinst.debhelper0000644000175000017500000000055712313357164020423 0ustar colincolin# Automatically added by dh_installinit if [ -x "/etc/init.d/aprx" ]; then update-rc.d aprx defaults >/dev/null if [ -n "$2" ]; then _dh_action=restart else _dh_action=start fi if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then invoke-rc.d aprx $_dh_action || exit $? else /etc/init.d/aprx $_dh_action || exit $? fi fi # End automatically added section aprx-2.08.svn593/debian/aprx.default0000644000175000017500000000027712005774513016231 0ustar colincolin# # STARTAPRX: start aprx on boot. Should be set to "yes" once you have # configured aprx. # STARTAPRX="no" # # Additional options that are passed to the Daemon. # DAEMON_OPTS="" aprx-2.08.svn593/debian/control0000644000175000017500000000125312305424767015315 0ustar colincolinSource: aprx Section: hamradio Priority: extra Maintainer: HAM APRX release maintenance Build-Depends: debhelper (>= 4), libssl-dev Standards-Version: 3.7.2 Package: aprx Architecture: any Depends: ${shlibs:Depends}, openssl Description: APRS Digipeater and iGate Aprx is an APRS specific Digipeater and iGate. It supports multiple KISS-TNCs on serial ports and listening to any kernel AX.25 network interfaces. . Additional features include a built-in "erlang-monitor" to analyze activity level of radio channels. . This software requires a valid (and unique) ham radio callsign to operate fully and is therefore useful mainly for licensed radio amateurs. aprx-2.08.svn593/debian/aprx.postrm.debhelper0000644000175000017500000000023112313357164020051 0ustar colincolin# Automatically added by dh_installinit if [ "$1" = "purge" ] ; then update-rc.d aprx remove >/dev/null || exit $? fi # End automatically added section aprx-2.08.svn593/debian/copyright0000644000175000017500000000001512005774513015632 0ustar colincolinSee LICENSE. aprx-2.08.svn593/debian/postinst.off0000644000175000017500000000164712032356321016263 0ustar colincolin#!/bin/sh -e action="$1" oldversion="$2" . /usr/share/debconf/confmodule db_version 2.0 umask 022 if [ "$action" != configure ] ; then exit 0 fi # functions setup_aprx_user() { if ! getent passwd aprsc >/dev/null; then echo "Creating user account: 'aprsc'" adduser --quiet --system --no-create-home --home /var/run/aprx --shell /usr/sbin/nologin --group aprx fi } fix_permissions() { : # chown aprx:aprx /opt/aprsc/logs /opt/aprsc/data } apparmor_config() { # Reload AppArmor profile APP_PROFILE="/etc/apparmor.d/sbin.aprx" if [ -f "$APP_PROFILE" ] && aa-status --enabled 2>/dev/null; then echo "Installing apparmor profile..." apparmor_parser -r -T -W "$APP_PROFILE" || true fi } # main # setup_aprx_user # fix_permissions apparmor_config # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 aprx-2.08.svn593/debian/files0000644000175000017500000000005012313357166014726 0ustar colincolinaprx_2.08.580-1_i386.deb hamradio extra aprx-2.08.svn593/debian/aprx.init0000644000175000017500000001133612032354720015540 0ustar colincolin#! /bin/sh ### BEGIN INIT INFO # Provides: aprx # Required-Start: $syslog $local_fs # Required-Stop: $syslog $local_fs # Should-Start: ax25ifs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: start and stop aprx # Description: Monitor and gateway radio amateur APRS radio network datagrams ### END INIT INFO # Do NOT "set -e" # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="aprx igate" NAME=aprx DAEMON=/usr/sbin/$NAME DAEMON_ARGS="" PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/$NAME ] && . /etc/default/$NAME # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.2-14) to ensure that this file is present # and status_of_proc is working. . /lib/lsb/init-functions # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 # Add code here, if necessary, that waits for the process to be ready # to handle requests from services started subsequently which depend # on this one. As a last resort, sleep for some time. } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON [ "$?" = 2 ] && return 2 # Many daemons don't delete their pidfiles when they exit. rm -f $PIDFILE return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { # # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME return 0 } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; #reload|force-reload) # # If do_reload() is not implemented then leave this commented out # and leave 'force-reload' as an alias for 'restart'. # #log_daemon_msg "Reloading $DESC" "$NAME" #do_reload #log_end_msg $? #;; restart|force-reload) # # If the "reload" option is implemented then remove the # 'force-reload' alias # log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 exit 3 ;; esac : aprx-2.08.svn593/debian/aprx.substvars0000644000175000017500000000004012313357166016630 0ustar colincolinshlibs:Depends=libc6 (>= 2.7-1) aprx-2.08.svn593/debian/aprx.prerm.debhelper0000644000175000017500000000041012313357164017651 0ustar colincolin# Automatically added by dh_installinit if [ -x "/etc/init.d/aprx" ] && [ "$1" = remove ]; then if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then invoke-rc.d aprx stop || exit $? else /etc/init.d/aprx stop || exit $? fi fi # End automatically added section aprx-2.08.svn593/debian/dirs0000644000175000017500000000010512021467264014563 0ustar colincolinetc/ etc/apparmor.d usr/sbin usr/share/man/man8 var/log/aprx var/run aprx-2.08.svn593/debian/rules0000755000175000017500000000365512032663045014771 0ustar colincolin#!/usr/bin/make -f # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 CFLAGS = -Wall -g ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) CFLAGS += -O0 else CFLAGS += -O2 endif configure: configure-stamp configure-stamp: dh_testdir # Add here commands to configure the package. ./configure --with-pthread --sbindir=/usr/sbin --sysconfdir=/etc \ --localstatedir=/var --mandir=/usr/share/man \ CC="gcc" \ CFLAGS="${CFLAGS}" \ AFLAGS="${CFLAGS} --noexecstack" \ LDFLAGS="${CFLAGS} -z noexecstack" touch configure-stamp build: build-stamp build-stamp: configure-stamp dh_testdir # Add here commands to compile the package. $(MAKE) #docbook-to-man debian/aprx.sgml > aprx.1 touch $@ clean: dh_testdir dh_testroot rm -f build-stamp configure-stamp # Add here commands to clean up after the build process. -$(MAKE) clean rm -f debian/aprx.logrotate dh_clean install: build dh_testdir dh_testroot dh_clean -k dh_installdirs # Add here commands to install the package into debian/aprx. $(MAKE) DESTDIR=$(CURDIR)/debian/aprx logrotate.aprx install cp logrotate.aprx debian/aprx.logrotate install -m 644 apparmor.aprx $(CURDIR)/debian/aprx/etc/apparmor.d/sbin.aprx # Build architecture-independent files here. binary-indep: build install # We have nothing to do by default. # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot # dh_installchangelogs ChangeLog dh_installdocs dh_installexamples # dh_install # dh_installmenu # dh_installdebconf dh_installlogrotate # dh_installemacsen # dh_installpam # dh_installmime # dh_python dh_installinit --restart-after-upgrade # dh_installcron # dh_installinfo dh_installman dh_link # dh_strip dh_compress dh_fixperms # dh_perl # dh_makeshlibs dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install configure aprx-2.08.svn593/svnversion-test.sh0000644000175000017500000000145612273250101016201 0ustar colincolin#!/bin/sh #set -x echo "svnversion-test.sh: $*" SV="$1" shift SVNVERSION=undef for x in /bin/svnversion /usr/bin/svnversion do if [ -x $x ] ; then SVNVERSION="`$x`" fi done if [ "$SVNVERSION" = "undef" -o "$SVNVERSION" = "Unversioned directory" ] ; then if [ -f SVNVERSION ] ; then echo "Can't pull SVNVERSION value from svn storage, pulling from SVNVERSION file.." SVNVERSION="`cat SVNVERSION`" fi else echo "$SVNVERSION" > SVNVERSION fi if [ "$SVNVERSION" != "$SV" ] ; then echo "Miss-match of '$SVNVERSION' vs. '$SV' -- aborting now, please rerun the make command." exit 1 fi X="`(echo -n $SVNVERSION | tr -d 0-9)`" if [ -n "$X" ] ; then echo "Mixed or modified tree: ($SVNVERSION), ARE YOU SURE ??." ; \ echo -n "Y/^C ? "; read var ; \ fi aprx-2.08.svn593/build-stamp0000644000175000017500000000000012313357163014613 0ustar colincolinaprx-2.08.svn593/PROTOCOLS0000644000175000017500000001745012005774517014003 0ustar colincolin Protocols spoken by APRX There are a few protocols spoken by the APRX: 1) APRS-IS interchange 2) Proprietary monitoring via shared memory segment 3) APRX-APRXC linkage APRS-IS interchange This protocol is fairly simple. http://wiki.ham.fi/IGate_properties APRS iGate Common Properties Communication is over TCP streams of text lines terminating at CR+LF pair. No CR or LF is permitted inside the text line, however. APRS-IS user authentication: user USERID pass PASSCODE vers VERS STRINGS \ filter FILTER STRINGS where 'USERID' is uppercase callsign plus possible "-SSID" tail. The 'PASSCODE' is calculated with semi-secret algorithm out of the uppercase callsign characters without '-' and SSID tail. The 'VERS STRINGS' are free to be anything, except string 'filter', and the 'FILTER STRINGS' are explained in document: http://www.aprs-is.net/ServerCmds.htm http://www.aprs-is.net/javAPRSSrvr/javaprsfilter.htm Messages: Lines beginning with '#' character are line noise (usually something that the server replies..) After successfull login, communication carries "TNC2" format APRS messages. Namely text encoding of AX.25 UI frames in what became known as "TNC2 monitor style": SOURCE>DESTIN:payload SOURCE>DESTIN,VIA,VIA:payload The SOURCE, DESTIN, and VIA fields are AX.25 address fields, and have "-SSID" value annexed if the SSID is not zero. Also in VIA-fields, if the "HAS BEEN DIGIPEATED" bit is set (AX.25 v2 protocol feature) a star ('*') character is appended. VIA-fields are separated by comma (',') from DESTIN, and each other. A double-colon (':') separates address data from payload. The payload is passed _AS_IS_ without altering any message content bytes, however ending at first CR or LF character encountered in the packet. APRS-RX iGate Basic Rules Packets with source addresses: NOCALL*, N0CALL*, WIDE*, TRACE*, TCP*, are dropped and not relayed to APRS-IS. Packets with VIA addresses: RFONLY, NOGATE, TCPIP, TCPXX are not to be relayed to APRS-IS. Sometimes the VIA fields have '*' on their tails. Packets with payload beginning with character '?' are not to be relayed to APRS-IS. Packets with payload beginning with character '}' are so called 3rd-party frames, and they are to be re-processed starting from character following the '}' character. When packet is sent to APRS-IS, the address gets appended either a "q-construct", or equivalent: ,qAR,gatecallsign ,gatecallsign,I The "qAR" et.al. are explained at: http://www.aprs-is.net/q.aspx APRS-TX iGate has additional rules: All rules: http://www.aprs-is.net/IGateDetails.aspx Specifically forbidden to relay to RF is "qAX", maybe some others too. (See: http://www.aprs-is.net/q.aspx) If the packet VIA-path received from APRS-IS contains TCPXX, NOGATE, RFONLY, then the packet is to be dropped, and not relayed to radio network. (Note: TCPIP is permitted.) Packets relayed from APRS-IS to radio must use so called 3rd-party format. Signature is '}' character. Rules on re-sending recently heard packets are a bit more complex, and are covered adequately in above referenced document. Proprietary monitoring mechanism There is a tool for monitoring channel activity. See aprx-stat(8) APRX-APRXC linkage (For a feature in planning...) With introduction of APRX-Cluster (APRXC) mode, multiple APRX instances are used as remote attachments to TNCs, and one central system runs more complicated business logic deciding which messages to pass where, what beacons to send, etc. The central business-logic server runs also connection with APRS-IS, and all things that it implies. Actually the APRX-Cluster-Server is APRX program version with embedded Perl for business logic. Some very flexibly configurable APRS digi software has practical limitations of what the config can do. Environmental pre-requisite: APRXC and APRX nodes are in NTP sync! The Protocol: Communication is of text lines over TCP stream, they are canonic Internet format ending with CRLF pairs. However no embedded CR, nor LF is permitted. (detail under study: UDP frames, or SCTP SEQPACKET ?) The protocol is bidirectional, and is intended to be connected from edge systems to cluster server. Upon receiving a connection the server sends a greeting containing time-varying string. This will be used by the connecting party to do authentication in APOP-style. The timestamps in this protocol are "U" format: sprintf(timestamp, "U%ld", (long)time(NULL)); which is to say, integer presentation of UNIX internal time in whole seconds. In following ">>" mean data sent by central system and received by the edge, whereas "<<" means data sent by edge system to central system. << (connection formation from edge to the server) >> Hello <++counter> some greeting string << LOGIN >> OK << SERVICE { RX | TX } >> OK The is formed by hex (lower case) encoding of 16 byte MD5() checksum over the whole greeting string (sans CRLF), plus userid, plus shared plaintext password. Replies can also be "FAIL", if "LOGIN" or "SERVICE" is somehow bad. The is local interface name in given node. The is radio channel bitrate. The are filter statements in APRS-IS style. The edge system compares every received timestamp (aside of the login-sequence above) with its internal time reference, and if the time varies more than 3 seconds from expected, the received frame is discarded. If APRXC Server notes that the time stamp it received is more than 3 seconds in future or in past, it rejects the message. (This does not apply to ERLANG verbs.) The APRXC server acknowledges every received message with: >> OK Link idle jabber is by done by both systems by sending "TIME" verbs. They are sent every 20 seconds since previous transmit of anything on the connection, and if either notes that link has not received anything for 120 seconds, the link is considered to be down, closed, and re-initiated. << TIME >> OK >> TIME << OK Either system can initiate the "heartbeat", sometimes even both may send the TIME verbs at the same time, but both are not required to do so. The edge system sends received APRS frames to the central system in "TNC2" format in a command: << APRS The is the data received from radio as is without any APRS-IS -type additional parameters inserted into address field. When central system wants that a TX capable edge system transmits something, it sends following message: >> APRSTX the edge system will transmit the frame as is (the APRXC will format the message to be ready for transmit.) If the edge system can not transmit on given interface, it reports: << FAILTX which results from APRXC and/or APRX software bug listing/flagging tx-interface to be non-tx-interface.. Otherwise it acks the packet with "OK" message. << OK Edge systems send 1 minute ERLANG data to central system with verb: << ERLANG \ [ ] >> OK (Long line is folded in UNIX-style in this documentation. Data is ACKed with OK.) The APRXC collates these to 1 minute, 10 minute and 60 minute datasets of given timestamp. The measured erlang-values are optional, if edge system can produce truthfull ones. The time-series collation database is persistent, for example RRD. aprx-2.08.svn593/cellmalloc.h0000644000175000017500000000235512305424772014750 0ustar colincolin/******************************************************************** * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * ********************************************************************/ #ifndef _CELLMALLOC_H_ #define _CELLMALLOC_H_ /* * cellmalloc() -- manages arrays of cells of data * */ typedef struct cellarena_t cellarena_t; extern cellarena_t *cellinit(const char *arenaname, const int cellsize, const int alignment, const int policy, const int createkb, const int minfree); #define CELLMALLOC_POLICY_FIFO 0 #define CELLMALLOC_POLICY_LIFO 1 #define CELLMALLOC_POLICY_NOMUTEX 2 extern void *cellmalloc(cellarena_t *cellarena); extern int cellmallocmany(cellarena_t *cellarena, void **array, const int numcells); extern void cellfree(cellarena_t *cellarena, void *p); extern void cellfreemany(cellarena_t *cellarena, void **array, const int numcells); #endif aprx-2.08.svn593/logrotate.aprx.in0000644000175000017500000000023312005774520015756 0ustar colincolin@VARLOG@/aprx-rf.log @VARLOG@/aprx.log @VARLOG@/dprs.log @VARLOG@/erlang.log { weekly rotate 4 compress missingok notifempty create 644 root adm } aprx-2.08.svn593/ssl.h0000644000175000017500000000450412273250100013422 0ustar colincolin #include "config.h" #ifdef HAVE_OPENSSL_SSL_H #define USE_SSL #endif #ifndef SSL_H #define SSL_H #ifdef USE_SSL #include #include #include #include #include /* ssl error codes, must match ssl_err_labels order */ #define SSL_VALIDATE_INTERNAL_ERROR -1 #define SSL_VALIDATE_CLIENT_CERT_UNVERIFIED -2 #define SSL_VALIDATE_NO_CLIENT_CERT -3 #define SSL_VALIDATE_CERT_NO_SUBJECT -4 #define SSL_VALIDATE_CERT_NO_CALLSIGN -5 #define SSL_VALIDATE_CERT_CALLSIGN_MISMATCH -6 struct client_t; struct worker_t; struct ssl_t { SSL_CTX *ctx; unsigned validate; }; struct ssl_connection_t { SSL *connection; unsigned handshaked:1; unsigned renegotiation:1; unsigned buffer:1; unsigned no_wait_shutdown:1; unsigned no_send_shutdown:1; unsigned validate; int ssl_err_code; }; #define NGX_SSL_SSLv2 0x0002 #define NGX_SSL_SSLv3 0x0004 #define NGX_SSL_TLSv1 0x0008 #define NGX_SSL_TLSv1_1 0x0010 #define NGX_SSL_TLSv1_2 0x0020 #define NGX_SSL_BUFFER 1 #define NGX_SSL_CLIENT 2 #define NGX_SSL_BUFSIZE 16384 /* string representations for error codes */ extern const char *ssl_strerror(int code); /* initialize and deinit the library */ extern int ssl_init(void); extern void ssl_atend(void); /* per-listener structure allocators */ extern struct ssl_t *ssl_alloc(void); extern void ssl_free(struct ssl_t *ssl); /* create context for listener, load certs */ extern int ssl_create(struct ssl_t *ssl, void *data); extern int ssl_certificate(struct ssl_t *ssl, const char *certfile, const char *keyfile); extern int ssl_ca_certificate(struct ssl_t *ssl, const char *cafile, int depth); /* create / free connection */ extern int ssl_create_connection(struct ssl_t *ssl, struct client_t *c, int i_am_client); extern void ssl_free_connection(struct client_t *c); /* validate a client certificate */ extern int ssl_validate_peer_cert_phase1(struct client_t *c); extern int ssl_validate_peer_cert_phase2(struct client_t *c); extern int ssl_write(struct worker_t *self, struct client_t *c); extern int ssl_writable(struct worker_t *self, struct client_t *c); extern int ssl_readable(struct worker_t *self, struct client_t *c); #else struct ssl_t { }; #define ssl_init(...) { } #define ssl_atend(...) { } #endif /* USE_SSL */ #endif /* SSL_H */ aprx-2.08.svn593/aprx-config.xsd0000644000175000017500000002104512005774520015416 0ustar colincolin The Aprx uses following configuration schema, which took its model very much from how Apache HTTPD configuration is done. To denote elements without < .. > around them as Apache does, we use "uri:apache:config" style bits, like: <ht:element> Because Apache's configuration predates XML and schemas, there has never been proper XML-like schema for it or anything alike it, nor is this intended to be validatable by XML tools. The overall syntax is line oriented, along with \ -characters at the end of line as continuation markers to append next line in place of that character plus the end-of-line character(s). Anywhere (except when previous line ends with \ -character) if the first non-white-space character is '#' the whole line is comment, and is discarded. Lines containing only white-space characters are also comments and are discarded. Presence of non-quoted '#' at the tails of parameter lines should also behave as comment, but it is not universal. Presence of extra parameter values is not always flagged as an error. The overall Aprx config contains parameter groups, and one top-level parameter label. The "mycall" parameter label must be before anything else. The <interface> and <aprsis> groups follow, and the rest that use those interfaces then follow. The <logging> group can be anywhere. Each group can occur multiple times. The Aprx configuration has flexible time interval definitions. A string of decimal numbers followed by multiplier character: 12 d 2 h 20 m 24 s 50 h 70 m 200 s 200 s 3 h Whatever notation is easiest to understand for the use. The multipliers are single characters and case insensitive. Embedding white-space is permitted for readability. ("d" = days, "h" = hours, "m" = minutes, "s" = seconds) This is ASCII representation of AX.25 callsign. Up to 6 upper-case letters (A-Z) and digits (0-9), then possibly a minus ('-') and decimal number 0 to 15. By convention a "-0" is never used. This is more relaxed form of AX.25 callsign: Regular expression: [a-zA-Z0-9]{1,6}($|-[a-zA-Z0-9]{1,2}) Or verbally: Up to 6 alphanumeric characters + optional tail of a minus ('-') followed by one or two alphanumeric characters. Also lowercase letters are allowed in alphanumerics. The APRSIS group defines communication parameters to APRS-IS network. The Login parameter for APRS-IS network. The Aprx will calculate correct authentication parameter for that login value. Defines server to which the Aprx connects for the APRS-IS service. serverhost [portnum] Chosen servers should be regional round-robin ones, see http://www.aprs2.net/ noam.aprs2.net soam.aprs2.net euro.aprs2.net asia.aprs2.net aunz.aprs2.net or the global pool: rotate.aprs2.net You SHOULD NOT connect to any specific one, as none of the servers are behind a load-balancing fail-over mechanisms. One white-space terminated parameter that is fed to APRS-IS server immediately after a login. If multiple filters are needed, they are defined with successive 'filter' parameter entries. The Logging group defines how the Aprx does logs. Without it, or any parameters inside it, no logging happens. aprx-2.08.svn593/README0000644000175000017500000000733212305424764013351 0ustar colincolin APRX v2.08 A multitalented APRS / DPRS / APRSIS "i-gate" with following properties: Config file (-f option) default is: /etc/aprx.conf Other runtime options are: -v, -d, -h/-? (verbout, debug and help) - Rx-IGate functionality works correctly - Tx-IGate functionality works correctly - Can do APRS New-N and generic AX.25 node digipeater functionality with transmitters - Has same-channel Viscous Digipeater functionality to not to digipeat at all, if during initial wait period the packet is heard again. - Has cross-interface Viscous Digipeater functionality to not to digipeat, if during the initial wait period the packet is heard on destination interface at least once, and at least once from other sources. - Can receive data from multiple receivers/modems on local machine serial ports, both classical and USB. - Can receive data from remote TCP stream connectable serial ports over the internet. - Understands on serial ports (local and remote TCP ones): - several KISS protocol variants, checksummed variants preferred - TNC2 debug style text (Rx-iGate receive only.) - D-STAR data side-channel "D-PRS" - Connects with one callsign-ssid pair to APRS-IS core for all received radio ports (the "mycall" parameter), but reports receiving radio port at each Rx-iGated packet - Knows that messages with following tokens in VIA fields of the path are not to be relayed into network: RFONLY, NOGATE, TCPIP, TCPXX - Knows that following source address prefixes are bogus and thus to be junked at Rx-iGate: WIDE, RELAY, TRACE, TCPIP, TCPXX, NOCALL, N0CALL (Actually these are string prefixes, so any WIDE*-* will block, etc.) - Has integrated D-PRS -> APRS/APRSIS Rx-iGate. Can even do D-PRS -> APRS RF conversion. This is experimental quality for "GPS" packets, the "GPS-A" is OK. - Does not require machine to have AX.25 protocol support internally! - On Linux machine with kernel internal AX.25 protocol support, does listen on internal AX.25 network in promiscuous form, and requires to be running as root to do that. Does not fail to start in case the port fails to open (running as non-root.) - Built-in "erlang-monitor" actually counts bytes per time interval (1 min, 10 min, and 20 min) on each receiving interface, including all that feed internal AX.25 network. - Telemetry reported erlang data is sent out every 20 minutes, and contains summarized data from 10 minute round-robin memory arrays. These are _not_ sent at exact 10 minutes of wall-clock, but exact 20 minutes from previous telemetry reporting, and first one is sent 20 minutes after program start. - Telemetry reported erlang data can be sent also over APRS radio port, but only for ports with valid AX.25 callsigns. See aprx-manual.pdf - The netbeacons are distributed timewise more evenly around the interval, and even the interval length is varied at random in between 20 and 30 minutes. Number of netbeacons are unlimited, but their minimum transmit interval is 3 seconds making the amount of beacons sendable in 20 minutes to be: 20*60/3 = 400. All will be sent, but the cycle will just take longer. - Source code is at SVN repository: http://repo.ham.fi/svn/aprx - A Wiki page of this package: http://wiki.ham.fi/Aprx.en - A google-group for Aprx: http://groups.google.com/group/aprx-software - This program is also compilable as "EMBEDDED" target without any statistics buffer required at the system disk (or RAM-disk). See the INSTALL file. by Matti Aarnio - OH2MQK - oh2mqk-at-sral-fi - 2007-2014 aprx-2.08.svn593/configure0000755000175000017500000052754612314016324014403 0ustar colincolin#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME= PACKAGE_TARNAME= PACKAGE_VERSION= PACKAGE_STRING= PACKAGE_BUGREPORT= PACKAGE_URL= ac_unique_file="aprx.h" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS LIBOBJS SVNVERSION_STRING VERSION_STRING LIBCRYPTO LIBSSL LIBRESOLV LIBSOCKET LIBGETADDRINFO LIBRT CCPTHREAD LIBPTHREAD LIBM CFLAGS_ARCH LD EGREP GREP CPP OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC SET_MAKE target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking with_embedded with_erlangstorage enable_igate enable_agwpe with_pthread with_pthreads with_openssl ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-igate Disable all IGate codes --enable-agwpe Enable AGWPE socket interface code. Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-embedded When desiring to target as embedded --with-erlangstorage When desiring a longer term backing storage on erlang datasets. NOT compatible with EMBEDDED, REQUIRES FILESYSTEM! --without-pthread When desiring not to use pthread subsystem --with-pthreads (mistyped pthread) When desiring use pthread subsystem --with-openssl=DIR Include OpenSSL support (requires OpenSSL >= 0.9.7) Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF configure generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_compute_int LINENO EXPR VAR INCLUDES # -------------------------------------------- # Tries to find the compile-time value of EXPR in a program that includes # INCLUDES, setting VAR accordingly. Returns whether the value could be # computed ac_fn_c_compute_int () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) >= 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_lo=0 ac_mid=0 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=$ac_mid; break else as_fn_arith $ac_mid + 1 && ac_lo=$as_val if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) < 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=-1 ac_mid=-1 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) >= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_lo=$ac_mid; break else as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done else ac_lo= ac_hi= fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=$ac_mid else as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done case $ac_lo in #(( ?*) eval "$3=\$ac_lo"; ac_retval=0 ;; '') ac_retval=1 ;; esac else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 static long int longval () { return $2; } static unsigned long int ulongval () { return $2; } #include #include int main () { FILE *f = fopen ("conftest.val", "w"); if (! f) return 1; if (($2) < 0) { long int i = longval (); if (i != ($2)) return 1; fprintf (f, "%ld", i); } else { unsigned long int i = ulongval (); if (i != ($2)) return 1; fprintf (f, "%lu", i); } /* Do not output a trailing newline, as this causes \r\n confusion on some platforms. */ return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : echo >>conftest.val; read $3 config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu VERSION="`cat VERSION`" PACKAGE=aprx { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi ac_config_headers="$ac_config_headers config.h" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" if test $ac_cv_c_compiler_gnu = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC needs -traditional" >&5 $as_echo_n "checking whether $CC needs -traditional... " >&6; } if ${ac_cv_prog_gcc_traditional+:} false; then : $as_echo_n "(cached) " >&6 else ac_pattern="Autoconf.*'x'" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include Autoconf TIOCGETP _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "$ac_pattern" >/dev/null 2>&1; then : ac_cv_prog_gcc_traditional=yes else ac_cv_prog_gcc_traditional=no fi rm -f conftest* if test $ac_cv_prog_gcc_traditional = no; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include Autoconf TCGETA _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "$ac_pattern" >/dev/null 2>&1; then : ac_cv_prog_gcc_traditional=yes fi rm -f conftest* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_gcc_traditional" >&5 $as_echo "$ac_cv_prog_gcc_traditional" >&6; } if test $ac_cv_prog_gcc_traditional = yes; then CC="$CC -traditional" fi fi if test -z "$LD" ; then LD="$CC" fi LD="$LD" MACHINE="`uname -m`" if test "$MACHINE" == "i686" -o "$MACHINE" == "i386"; then CFLAGS_ARCH="-march=i686" fi OS="`uname`" if test "$OS" == "Darwin"; then CFLAGS_ARCH="" fi AX_CHECK_GNU_MAKE() { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in time.h sys/time.h stdlib.h stddef.h stdint.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in string.h strings.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in pty.h do : ac_fn_c_check_header_mongrel "$LINENO" "pty.h" "ac_cv_header_pty_h" "$ac_includes_default" if test "x$ac_cv_header_pty_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_PTY_H 1 _ACEOF fi done for ac_header in pthread.h do : ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default" if test "x$ac_cv_header_pthread_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_PTHREAD_H 1 _ACEOF fi done for ac_header in alloca.h do : ac_fn_c_check_header_mongrel "$LINENO" "alloca.h" "ac_cv_header_alloca_h" "$ac_includes_default" if test "x$ac_cv_header_alloca_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_ALLOCA_H 1 _ACEOF $as_echo "#define HAVE_ALLOCA_H 1" >>confdefs.h fi done for ac_header in poll.h do : ac_fn_c_check_header_mongrel "$LINENO" "poll.h" "ac_cv_header_poll_h" "$ac_includes_default" if test "x$ac_cv_header_poll_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_POLL_H 1 _ACEOF $as_echo "#define HAVE_POLL_H 1" >>confdefs.h fi done for ac_header in sys/epoll.h do : ac_fn_c_check_header_mongrel "$LINENO" "sys/epoll.h" "ac_cv_header_sys_epoll_h" "$ac_includes_default" if test "x$ac_cv_header_sys_epoll_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SYS_EPOLL_H 1 _ACEOF $as_echo "#define HAVE_SYS_EPOLL_H 1" >>confdefs.h fi done for ac_header in netinet/sctp.h do : ac_fn_c_check_header_mongrel "$LINENO" "netinet/sctp.h" "ac_cv_header_netinet_sctp_h" "$ac_includes_default" if test "x$ac_cv_header_netinet_sctp_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_NETINET_SCTP_H 1 _ACEOF $as_echo "#define HAVE_NETINET_SCTP_H 1" >>confdefs.h fi done for ac_header in stdarg.h varargs.h sys/wait.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in vprintf do : ac_fn_c_check_func "$LINENO" "vprintf" "ac_cv_func_vprintf" if test "x$ac_cv_func_vprintf" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_VPRINTF 1 _ACEOF ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt" if test "x$ac_cv_func__doprnt" = xyes; then : $as_echo "#define HAVE_DOPRNT 1" >>confdefs.h fi fi done { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** Using C compiler: $CC" >&5 $as_echo "** Using C compiler: $CC" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** Using CFLAGS: $CFLAGS" >&5 $as_echo "** Using CFLAGS: $CFLAGS" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** Using CPPDEP: $CPPDEP" >&5 $as_echo "** Using CPPDEP: $CPPDEP" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if ${ac_cv_c_bigendian+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5 $as_echo_n "checking size of void *... " >&6; } if ${ac_cv_sizeof_void_p+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default"; then : else if test "$ac_cv_type_void_p" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (void *) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_void_p=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5 $as_echo "$ac_cv_sizeof_void_p" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_VOID_P $ac_cv_sizeof_void_p _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of short" >&5 $as_echo_n "checking size of short... " >&6; } if ${ac_cv_sizeof_short+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (short))" "ac_cv_sizeof_short" "$ac_includes_default"; then : else if test "$ac_cv_type_short" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (short) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_short=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_short" >&5 $as_echo "$ac_cv_sizeof_short" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_SHORT $ac_cv_sizeof_short _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int" >&5 $as_echo_n "checking size of int... " >&6; } if ${ac_cv_sizeof_int+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default"; then : else if test "$ac_cv_type_int" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (int) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_int=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5 $as_echo "$ac_cv_sizeof_int" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_INT $ac_cv_sizeof_int _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5 $as_echo_n "checking size of long... " >&6; } if ${ac_cv_sizeof_long+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default"; then : else if test "$ac_cv_type_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (long) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_long=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5 $as_echo "$ac_cv_sizeof_long" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_LONG $ac_cv_sizeof_long _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of double" >&5 $as_echo_n "checking size of double... " >&6; } if ${ac_cv_sizeof_double+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (double))" "ac_cv_sizeof_double" "$ac_includes_default"; then : else if test "$ac_cv_type_double" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (double) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_double=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_double" >&5 $as_echo "$ac_cv_sizeof_double" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_DOUBLE $ac_cv_sizeof_double _ACEOF cat >>confdefs.h <<_ACEOF #define CONFIGURE_CMD "CC='$CC' CFLAGS='$CFLAGS' $0 $ac_configure_args" _ACEOF for ac_func in atan2f do : ac_fn_c_check_func "$LINENO" "atan2f" "ac_cv_func_atan2f" if test "x$ac_cv_func_atan2f" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_ATAN2F 1 _ACEOF else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for atan2f in -lm" >&5 $as_echo_n "checking for atan2f in -lm... " >&6; } if ${ac_cv_lib_m_atan2f+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char atan2f (); int main () { return atan2f (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_m_atan2f=yes else ac_cv_lib_m_atan2f=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_atan2f" >&5 $as_echo "$ac_cv_lib_m_atan2f" >&6; } if test "x$ac_cv_lib_m_atan2f" = xyes; then : LIBM="-lm" fi fi done for ac_func in memchr memrchr gettimeofday do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in openpty do : ac_fn_c_check_func "$LINENO" "openpty" "ac_cv_func_openpty" if test "x$ac_cv_func_openpty" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_OPENPTY 1 _ACEOF else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openpty in -lutil" >&5 $as_echo_n "checking for openpty in -lutil... " >&6; } if ${ac_cv_lib_util_openpty+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char openpty (); int main () { return openpty (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_util_openpty=yes else ac_cv_lib_util_openpty=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_openpty" >&5 $as_echo "$ac_cv_lib_util_openpty" >&6; } if test "x$ac_cv_lib_util_openpty" = xyes; then : $as_echo "#define HAVE_OPENPTY 1" >>confdefs.h LIBS="$LIBS -lutil" fi fi done $as_echo "#define EMBEDDED 1" >>confdefs.h # Check whether --with-embedded was given. if test "${with_embedded+set}" = set; then : withval=$with_embedded; $as_echo "#define EMBEDDED 1" >>confdefs.h EMBEDDED=1 fi # Check whether --with-erlangstorage was given. if test "${with_erlangstorage+set}" = set; then : withval=$with_erlangstorage; $as_echo "#define ERLANGSTORAGE 1" >>confdefs.h ERLANGSTORAGE=1 fi # Check whether --enable-igate was given. if test "${enable_igate+set}" = set; then : enableval=$enable_igate; if test "${enable_igate}" = no ; then $as_echo "#define DISABLE_IGATE 1" >>confdefs.h fi fi # Check whether --enable-agwpe was given. if test "${enable_agwpe+set}" = set; then : enableval=$enable_agwpe; if test "${enable_agwpe}" != no ; then $as_echo "#define ENABLE_AGWPE 1" >>confdefs.h fi fi # Check whether --with-pthread was given. if test "${with_pthread+set}" = set; then : withval=$with_pthread; $as_echo "#define DISABLE_PTHREAD 1" >>confdefs.h DISABLE_PTHREAD=1 else $as_echo "#define ENABLE_PTHREAD 1" >>confdefs.h ENABLE_PTHREAD=1 fi # Check whether --with-pthreads was given. if test "${with_pthreads+set}" = set; then : withval=$with_pthreads; $as_echo "#define ENABLE_PTHREAD 1" >>confdefs.h ENABLE_PTHREAD=1 fi if test "${ENABLE_PTHREAD}" = "1" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: The --without-pthread option is not set, looking for pthread_create() function." >&5 $as_echo "The --without-pthread option is not set, looking for pthread_create() function." >&6; } have_pthread=no if test $have_pthread = no; then t_oldLibs="$LIBS" LIBS="$LIBS -pthread" t_oldCflags="$CFLAGS" CFLAGS="$CFLAGS -pthread" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { pthread_t pt; pthread_attr_t pat; int rc = pthread_create(&pt, &pat, NULL, NULL) ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : have_pthread=yes;{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Have pthread_create()" >&5 $as_echo "Have pthread_create()" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Not have pthread_create()" >&5 $as_echo "Not have pthread_create()" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $have_pthread = no ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: Failure at HAVE_PTHREAD_CREATE" >&5 $as_echo "Failure at HAVE_PTHREAD_CREATE" >&6; } else $as_echo "#define HAVE_PTHREAD_CREATE 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: Success at HAVE_PTHREAD_CREATE" >&5 $as_echo "Success at HAVE_PTHREAD_CREATE" >&6; } LIBPTHREAD="-pthread" CCPTHREAD="-pthread" fi LIBS="$t_oldLibs" CFLAGS="$t_oldCflags" fi if test $have_pthread = no; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: Failure at HAVE_PTHREAD_CREATE, trying second way." >&5 $as_echo "Failure at HAVE_PTHREAD_CREATE, trying second way." >&6; } t_oldLibs="$LIBS" LIBS="$LIBS -lpthread" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { pthread_t pt; pthread_attr_t pat; int rc = pthread_create(&pt, &pat, NULL, NULL) ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : have_pthread=yes;{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Have pthread_create()" >&5 $as_echo "Have pthread_create()" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Not have pthread_create()" >&5 $as_echo "Not have pthread_create()" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $have_pthread = no ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: Failure at HAVE_PTHREAD_CREATE" >&5 $as_echo "Failure at HAVE_PTHREAD_CREATE" >&6; } else $as_echo "#define HAVE_PTHREAD_CREATE 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: Success at HAVE_PTHREAD_CREATE" >&5 $as_echo "Success at HAVE_PTHREAD_CREATE" >&6; } LIBPTHREAD="-lpthread" CCPTHREAD="" fi LIBS="$t_oldLibs" CFLAGS="$t_oldCflags" fi if test $have_pthread = no; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: Still failure at HAVE_PTHREAD_CREATE, Run out of ways to set it up." >&5 $as_echo "Still failure at HAVE_PTHREAD_CREATE, Run out of ways to set it up." >&6; } fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 $as_echo_n "checking for library containing clock_gettime... " >&6; } if ${ac_cv_search_clock_gettime+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char clock_gettime (); int main () { return clock_gettime (); ; return 0; } _ACEOF for ac_lib in '' rt; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_clock_gettime=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_clock_gettime+:} false; then : break fi done if ${ac_cv_search_clock_gettime+:} false; then : else ac_cv_search_clock_gettime=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 $as_echo "$ac_cv_search_clock_gettime" >&6; } ac_res=$ac_cv_search_clock_gettime if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" $as_echo "#define HAVE_CLOCK_GETTIME 1" >>confdefs.h fi if test "$ac_cv_search_clock_gettime" = "-lrt"; then LIBRT="-lrt" fi for ac_func in getaddrinfo do : ac_fn_c_check_func "$LINENO" "getaddrinfo" "ac_cv_func_getaddrinfo" if test "x$ac_cv_func_getaddrinfo" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_GETADDRINFO 1 _ACEOF else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for getaddrinfo in -lnsl" >&5 $as_echo_n "checking for getaddrinfo in -lnsl... " >&6; } if ${ac_cv_lib_nsl_getaddrinfo+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lnsl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char getaddrinfo (); int main () { return getaddrinfo (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_nsl_getaddrinfo=yes else ac_cv_lib_nsl_getaddrinfo=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_getaddrinfo" >&5 $as_echo "$ac_cv_lib_nsl_getaddrinfo" >&6; } if test "x$ac_cv_lib_nsl_getaddrinfo" = xyes; then : LIBGETADDRINFO="-lnsl" fi fi done # # We check for various libraries # - SysVr4 style of "-lsocket" at first (unless in libc) # The hallmark is connect() routine (we presume) # ac_cv_libsocket_both=1 ac_fn_c_check_func "$LINENO" "connect" "ac_cv_func_connect" if test "x$ac_cv_func_connect" = xyes; then : ac_cv_libsocket_both=0 fi ac_fn_c_check_func "$LINENO" "gethostbyname" "ac_cv_func_gethostbyname" if test "x$ac_cv_func_gethostbyname" = xyes; then : ac_cv_libsocket_both=0 fi if test "$ac_cv_libsocket_both" = 1 ; then # Check cache if test "$ac_cv_func_socket_lxnet" = yes ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: need -lxnet library (cached)" >&5 $as_echo "need -lxnet library (cached)" >&6; } LIBSOCKET="-lnsl -lsocket -lxnet" else if test "$ac_cv_func_socket_lsocket" = yes ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: need -lsocket library (cached)" >&5 $as_echo "need -lsocket library (cached)" >&6; } LIBSOCKET="-lsocket" if test "$ac_cv_func_gethostbyname_lnsl" = yes ; then LIBSOCKET="-lnsl -lsocket" fi else # Well, will this work ? SysVR4, but not Sun Solaris ? { $as_echo "$as_me:${as_lineno-$LINENO}: checking for connect in -lxnet" >&5 $as_echo_n "checking for connect in -lxnet... " >&6; } if ${ac_cv_lib_xnet_connect+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lxnet $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char connect (); int main () { return connect (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_xnet_connect=yes else ac_cv_lib_xnet_connect=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xnet_connect" >&5 $as_echo "$ac_cv_lib_xnet_connect" >&6; } if test "x$ac_cv_lib_xnet_connect" = xyes; then : LIBSOCKET="-lnsl -lsocket -lxnet" ac_cv_func_socket_lsocket=no ac_cv_func_socket_lxnet=yes else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for connect in -lsocket" >&5 $as_echo_n "checking for connect in -lsocket... " >&6; } if ${ac_cv_lib_socket_connect+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char connect (); int main () { return connect (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_socket_connect=yes else ac_cv_lib_socket_connect=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_connect" >&5 $as_echo "$ac_cv_lib_socket_connect" >&6; } if test "x$ac_cv_lib_socket_connect" = xyes; then : LIBSOCKET="-lsocket" ac_cv_func_socket_lsocket=yes else ac_cv_func_socket_lsocket=no fi if test "$ac_cv_func_socket_lsocket" = yes ; then t_oldLibs="$LIBS" LIBS="$LIBS -lsocket $LIBRESOLV" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { gethostbyname(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : else LIBS="$LIBS -lnsl" # Add this Solaris library.. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { gethostbyname(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : LIBSOCKET="-lsocket -lnsl" ac_cv_func_gethostbyname_lnsl=yes else as_fn_error $? "Weird, '$LIBS' not enough to find gethostnyname() ?!" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$t_oldLibs" fi fi fi fi fi if test "x$LIBRESOLV" = "x"; then # Ok, No -lresolv, is this enough for the _res to appear ? t_oldLibs="$LIBS" LIBS="$LIBS $LIBSOCKET" ac_cv_var__res_options=no # This following is for IRIX6.4, and I sincerely hope it # will not fail on other systems... It did! It did! # Many systems don't have idemponent headers, they need specific # includes before latter ones, or the latter ones won't be successful... cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include int main () { _res.options = RES_INIT; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_var__res_options=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext; if test "$ac_cv_var__res_options" != "yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: Can't do without -lresolv to link resolver's _res.options" >&5 $as_echo "Can't do without -lresolv to link resolver's _res.options" >&6; } LIBS="$LIBS -lresolv" fi LIBS="$t_oldLibs" fi # See about the routines that possibly exist at the libraries.. LIBS="$t_oldLibs $LIBSOCKET" for ac_func in socket socketpair do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done LIBS="$t_oldLibs" if test "$ac_cv_func_socket" = no -a "$LIBSOCKET" != ""; then LIBS="$LIBS $LIBSOCKET" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { socket(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_func_socket=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test $ac_cv_func_socket = yes; then $as_echo "#define HAVE_SOCKET 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: Has socket() when using $LIBSOCKET" >&5 $as_echo "Has socket() when using $LIBSOCKET" >&6; } fi LIBS="$t_oldLibs" fi if test "$ac_cv_func_socketpair" = no -a "$LIBSOCKET" != ""; then LIBS="$LIBS $LIBSOCKET" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { socketpair(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_func_socketpair=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test $ac_cv_func_socketpair = yes; then $as_echo "#define HAVE_SOCKETPAIR 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: Has socketpair() when using $LIBSOCKET" >&5 $as_echo "Has socketpair() when using $LIBSOCKET" >&6; } fi LIBS="$t_oldLibs" fi # Check whether --with-openssl was given. if test "${with_openssl+set}" = set; then : withval=$with_openssl; with_openssl=$withval else with_openssl=no fi if test "$with_openssl" != "no" ; then for ac_header in openssl/ssl.h do : ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default" if test "x$ac_cv_header_openssl_ssl_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_OPENSSL_SSL_H 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing TLSv1_server_method" >&5 $as_echo_n "checking for library containing TLSv1_server_method... " >&6; } if ${ac_cv_search_TLSv1_server_method+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char TLSv1_server_method (); int main () { return TLSv1_server_method (); ; return 0; } _ACEOF for ac_lib in '' ssl; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_TLSv1_server_method=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_TLSv1_server_method+:} false; then : break fi done if ${ac_cv_search_TLSv1_server_method+:} false; then : else ac_cv_search_TLSv1_server_method=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_TLSv1_server_method" >&5 $as_echo "$ac_cv_search_TLSv1_server_method" >&6; } ac_res=$ac_cv_search_TLSv1_server_method if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" $as_echo "#define HAVE_TLSV1_SERVER_METHOD 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing X509_free" >&5 $as_echo_n "checking for library containing X509_free... " >&6; } if ${ac_cv_search_X509_free+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char X509_free (); int main () { return X509_free (); ; return 0; } _ACEOF for ac_lib in '' crypto; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_X509_free=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_X509_free+:} false; then : break fi done if ${ac_cv_search_X509_free+:} false; then : else ac_cv_search_X509_free=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_X509_free" >&5 $as_echo "$ac_cv_search_X509_free" >&6; } ac_res=$ac_cv_search_X509_free if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" $as_echo "#define HAVE_X509_FREE 1" >>confdefs.h fi if test "$ac_cv_search_TLSv1_server_method" = "-lssl"; then LIBSSL="-lssl" fi if test "$ac_cv_search_X509_free" = "-lcrypto"; then LIBCRYPTO="-lcrypto" fi fi t_vers="`cat VERSION`" VERSION_STRING="`cat VERSION`" t_vers="`cat SVNVERSION`" SVNVERSION_STRING="$t_vers" ac_config_files="$ac_config_files Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by $as_me, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi aprx-2.08.svn593/historydb.h0000644000175000017500000000606312305424764014651 0ustar colincolin/******************************************************************** * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * ********************************************************************/ /* * The historydb contains positional packet data in form of: * - position packet * - objects * - items * Keying varies, origination callsign of positions, name * for object/item. * * Inserting does incidential cleanup scanning while traversing * hash chains. * * In APRS-IS there are about 25 000 distinct callsigns or * item or object names with position information PER WEEK. * DB lifetime of 48 hours cuts that down a bit more. * Memory usage is around 3-4 MB * * -------------- * * On Tx-IGate the number of distinct callsigns is definitely * lower... * */ #ifndef __HISTORYDB_H__ #define __HISTORYDB_H__ #ifdef HAVE_STDINT_H # include #endif #define HISTORYDB_HASH_MODULO 128 /* fold bits: 7 & 14 */ struct pbuf_t; // forward declarator struct historydb_t; // forward.. typedef struct history_cell_t { struct history_cell_t *next; struct historydb_t *db; time_t arrivaltime; time_t positiontime; // When last position was received time_t *last_heard; // Usually points to last_heard_buf[] time_t last_heard_buf[MAX_IF_GROUP]; float tokenbucket; // Source callsign specific TokenBucket filter // Digi allocates HistoryDb per transmitter. uint16_t packettype; uint16_t flags; uint16_t packetlen; uint8_t keylen; char key[CALLSIGNLEN_MAX+2]; float lat, coslat, lon; uint32_t hash1; char *packet; char packetbuf[170]; /* Maybe a dozen packets are bigger than 170 bytes long out of some 17 000 .. */ } history_cell_t; typedef struct historydb_t { struct history_cell_t *hash[HISTORYDB_HASH_MODULO]; // monitor counters and gauges long historydb_inserts; long historydb_lookups; long historydb_hashmatches; long historydb_keymatches; long historydb_cellgauge; long historydb_noposcount; } historydb_t; extern void historydb_init(void); extern historydb_t *historydb_new(void); extern void historydb_dump(const historydb_t *, FILE *fp); extern void historydb_atend(void); extern int historydb_prepoll(struct aprxpolls *app); extern int historydb_postpoll(struct aprxpolls *app); /* insert and lookup... */ extern history_cell_t *historydb_insert(historydb_t *db, const struct pbuf_t*); extern history_cell_t *historydb_insert_(historydb_t *, const struct pbuf_t *, const int); extern history_cell_t *historydb_insert_heard(historydb_t *db, const struct pbuf_t*); extern history_cell_t *historydb_lookup(historydb_t *db, const char *keybuf, const int keylen); #endif aprx-2.08.svn593/VERSION0000644000175000017500000000002112320106141013504 0ustar colincolinaprx-2.08.svn593 aprx-2.08.svn593/rpm/0000755000175000017500000000000012320106142013242 5ustar colincolinaprx-2.08.svn593/rpm/aprx.default0000644000175000017500000000027712005774514015606 0ustar colincolin# # STARTAPRX: start aprx on boot. Should be set to "yes" once you have # configured aprx. # STARTAPRX="no" # # Additional options that are passed to the Daemon. # DAEMON_OPTS="" aprx-2.08.svn593/rpm/aprx.service0000644000175000017500000000031412043601161015577 0ustar colincolin[Unit] Description=Amateur Radio APRS Gateway & Digipeater Documentation=man:aprx(8) [Service] Type=simple ExecStart=/usr/sbin/aprx # doesn't do any internal reload [Install] WantedBy=multi-user.target aprx-2.08.svn593/rpm/aprx.init0000755000175000017500000000312112043601161015104 0ustar colincolin#!/bin/bash # # chkconfig: - 16 84 # description: Start up a receive only APRS igate # processname: aprx # config: /etc/sysconfig/aprx ### BEGIN INIT INFO -- debian style: # Provides: aprx # Required-Start: $syslog $local_fs # Required-Stop: $syslog $local_fs # Default-Stop: 0 1 6 # Short-Description: start and stop aprx # Description: Monitor and gateway radio amateur APRS radio network datagrams ### END INIT INFO # source function library . /etc/rc.d/init.d/functions # Check that networking is up. [ "${NETWORKING}" = "no" ] && exit 0 # defaults PATH=/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/sbin/aprx NAME=aprx DESC="aprx igate" # source the config, if it exists if [ -f /etc/sysconfig/aprx ] ; then . /etc/sysconfig/aprx fi test -x $DAEMON || exit 0 if [ "$STARTAPRX" != "yes" ];then echo "Starting of $NAME not enabled in /etc/sysconfig/$NAME." exit 0 fi set -e case "$1" in start) echo -n $"Starting aprx server: " $DAEMON disown -ar usleep 500000 status $NAME &> /dev/null && echo_success || echo_failure RETVAL=$? [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$NAME echo ;; stop) echo -n $"Shutting down aprx server: " killproc $DAEMON RETVAL=$? [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$NAME echo_success ;; restart|reload) $0 stop $0 start RETVAL=$? ;; condrestart) if [ -f /var/lock/subsys/$NAME ]; then $0 stop $0 start fi RETVAL=$? ;; status) status $NAME RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}" exit 1 esac exit $RETVAL aprx-2.08.svn593/ViscousDigipeater.README0000644000175000017500000000453312005774517017002 0ustar colincolin Viscous Digipeater in APRX The Viscous Digipeater is variation of AX.25 Digipeater, which puts arriving packets into a time delay probation pipe (up to 9 seconds), before digipeating them. One can use a Viscous Digipeater as a fill-in system, which does not repeat on band a packet that it hears having already been repeated. It does have also benefits on Tx-IGate, which will not re-transmit to band something that already was heard there within the small window of a few seconds that the probation pipe takes. The trick is that duplicate accounting is done right up front at arrival packet arrival time, and depending on what kind of source a similar packet arrives from (or none at all), that delayed packet may get bumped off the delay pipe, or even be sent out. The rules are a bit more complicated than for plain simple digipeater, but not impossibly so: Packets arriving from non-viscous sources trump those waiting in viscous queues. First one arriving will be transmitted, unless the viscous queue has no longer this packet (but it was there.) ( delayed_seen > 0, seen == 1, pbuf == NULL -> drop this ) ( delayed_seen > 0, seen == 1, pbuf != NULL -> clean pbuf, transmit this ) ( delayed_seen == 0, seen == 1 -> transmit this ) Subsequent packets arriving from non-viscous sources are dropped as duplicates ( seen > 1 -> drop this ) Packets arriving from any viscous source are dropped, if there already was some direct delivery packet ( delayed_seen > 0, seen > 0 -> drop ) First packet arriving from any viscous source is put on viscous queue, unless there was non-viscous packet previously. ( delayed_seen == 1, seen == 0 -> put this on viscous queue ) Then among viscous sources: - "Transmitter" kind source: an which is same as that of 's transmitter . - "Elsewhere" kind source: an which is some other than that of transmitter's, but has viscous-delay > 0 Account the number of viscous sourced packets sourced from "transmitter" if (source_is_transmitter) seen_on_transmitter += 1; For second and subsequent viscous sourced packets, if any of observed packets came from transmitter (seen_on_transmitter > 0), then drop current packet, and clear possible viscous queued pbuf. Matti Aarnio, OH2MQK, 2009-10-21 aprx-2.08.svn593/timestamp.c0000644000175000017500000001313712005774517014641 0ustar colincolin#include "aprx.h" /* Time Base Conversion Macros * * The NTP timebase is 00:00 Jan 1 1900. The local * time base is 00:00 Jan 1 1970. Convert between * these two by added or substracting 70 years * worth of time. Note that 17 of these years were * leap years. */ #define TIME_BASEDIFF (((70U*365U + 17U) * 24U*3600U)) #define TIME_NTP_TO_LOCAL(t) ((t)-TIME_BASEDIFF) #define TIME_LOCAL_TO_NTP(t) ((t)+TIME_BASEDIFF) typedef struct ntptime { uint32_t seconds; uint32_t fraction; } ntptime_t; uint64_t unix_tv_to_ntp(struct timeval *tv) { // Reciprocal conversion of tv_usec to fractional NTP seconds // Multiply tv_usec by (2^64)/1_000_000 // GCC optimized this nicely on i386 uint64_t fract = 18446744073709ULL * (uint32_t)(tv->tv_usec); // Scale it back by 32 bit positions fract >>= 32; // Straight-forward conversion of tv_sec to NTP seconds uint64_t ntptime = TIME_LOCAL_TO_NTP(tv->tv_sec); ntptime <<= 32; return ntptime + fract; } void unix_tv_to_ntp4(struct timeval *tv, ntptime_t *ntp) { // Reciprocal conversion of tv_usec to fractional NTP seconds // Multiply tv_usec by ((2^64)/1_000_000) / (2^32) // GCC optimized this nicely on i386, and 64-bit machines uint32_t fract = (18446744073709ULL * (uint32_t)(tv->tv_usec)) >> 32; // // movl 4(%ebx), %eax // imull $4294, %eax, %esi ;; 32*32->32 --> %esi // movl $-140462611, %edi // mull %edi ;; 32*32->64 --> %edx:eax // addl %edx, %esi ;; sum %esi + %edx // ntp->fraction = fract; // Straight-forward conversion of tv_sec to NTP seconds ntp->seconds = TIME_LOCAL_TO_NTP(tv->tv_sec); } void unix_tv_to_ntp4a(struct timeval *tv, ntptime_t *ntp) { // Reciprocal conversion of tv_usec to fractional NTP seconds // Multiply tv_usec by ((2^64)/1_000_000) / (2^32) // GCC optimizes this slightly better for ARM, than ntp4() // .. for i386 ntp4() and ntp4a() are equal. uint64_t fract = 18446744073709ULL * (uint32_t)(tv->tv_usec); // Scale it back by 32 bit positions fract >>= 32; ntp->fraction = (uint32_t)fract; // Straight-forward conversion of tv_sec to NTP seconds ntp->seconds = TIME_LOCAL_TO_NTP(tv->tv_sec); } uint64_t unix_tv_to_ntp2(struct timeval *tv) { uint64_t tt = TIME_LOCAL_TO_NTP(tv->tv_sec); tt <<= 32; uint64_t tu = tv->tv_usec; tu <<= 32; // Following causes gcc to call __udivdi3() // on 32-bit machines tu /= 1000000; // Fixed point scaling.. return (tt + tu); } // static const double usec2NtpFract = 4294.9672960D; // 2^32 / 1E6 uint64_t unix_tv_to_ntp3(struct timeval *tv) { uint64_t tt = TIME_LOCAL_TO_NTP(tv->tv_sec); tt <<= 32; // FP math is bad on embedded systems... // double fract = usec2NtpFract * (uint32_t)tv->tv_usec; // tt += (int64_t)fract; return tt; } static const char *BASE64EncodingDictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" "+/"; void encode_aprsis_ntptimestamp(uint64_t ntptime, char timestamp[8]) { int i; ntptime >>= 22; // scale to 1/1024 seconds for (i = 6; i >= 0; --i) { int n = (((int)ntptime) & 0x3F); // lowest 6 bits // printf(" [n=%d]\n", n); ntptime >>= 6; timestamp[i] = BASE64EncodingDictionary[n]; } timestamp[7] = 0; } static const int8_t BASE64DecodingDictionary[128] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ' ', '!', '"', '#' -1, -1, -1, -1, // '$', '%', '&'', '\'' -1, -1, -1, 62, // '(', ')', '*', '+', -1, -1, -1, 63, // ',', '-', '.', '/' 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0' .. '9' -1, -1, -1, -1, -1, -1, // ':', ';', '<', '=', '>', '?' -1, 0, 1, 2, 3, 4, 5, 6, // '@', 'A' .. 'G' 7, 8, 9, 10, 11, 12, 13, 14, // 'H' .. 'O' 15, 16, 17, 18, 19, 20, 21, 22, // 'P' .. 'W' 23, 24, 25, -1, -1, -1, -1, -1, // 'X'..'Z', '[', '\\', ']', '^', '_' -1, 26, 27, 28, 29, 30, 31, 32, // '`', 'a' .. 'g' 33, 34, 35, 36, 37, 38, 39, 40, // 'h' .. 'o' 41, 42, 43, 44, 45, 46, 47, 48, // 'p' .. 'w' 49, 50, 51, -1, -1, -1, -1, -1 }; // 'x'..'z', ... int decode_aprsis_ntptimestamp(char timestamp[8], uint64_t *ntptimep) { uint64_t ntptime = 0; int i, n; char c; for (i = 0; i < 7; ++i) { c = timestamp[i]; if (c <= 0 || c > 127) return -1; // BARF! n = BASE64DecodingDictionary[(int)c]; // printf(" [n=%d]\n", n); if (n < 0) { // Should not happen! return -1; // Decode fail! } ntptime <<= 6; ntptime |= n; } ntptime <<= 22; *ntptimep = ntptime; return 0; // Decode OK } #ifdef TESTING int main(int argc, char *argv[]) { struct timeval tv; char timestamp[8]; uint64_t ntptime; ntptime_t ntp_time; // gettimeofday(&tv, NULL); // Example time.. (refvalue: NTPseconds!) tv.tv_sec = TIME_NTP_TO_LOCAL(3484745636U); tv.tv_usec = 709603U; ntptime = unix_tv_to_ntp(&tv); printf("NTPtime1 = %08x.%08x \n", (uint32_t)(ntptime >> 32), (uint32_t)ntptime); ntptime = unix_tv_to_ntp2(&tv); printf("NTPtime2 = %08x.%08x \n", (uint32_t)(ntptime >> 32), (uint32_t)ntptime); // ntptime = unix_tv_to_ntp3(&tv); // printf("NTPtime3 = %08x.%08x \n", (uint32_t)(ntptime >> 32), (uint32_t)ntptime); unix_tv_to_ntp4(&tv, &ntp_time); printf("NTPtime4 = %08x.%08x \n", ntp_time.seconds, ntp_time.fraction); encode_aprsis_ntptimestamp( ntptime, timestamp ); printf("Timestamp = %s\n", timestamp); int rc = decode_aprsis_ntptimestamp( timestamp, &ntptime ); printf("Decode rc=%d\n", rc); printf("NTPtime = %08x.%08x \n", (uint32_t)(ntptime >> 32), (uint32_t)ntptime); return 0; } #endif aprx-2.08.svn593/VER0000644000175000017500000000000412200543121013014 0ustar colincolin542 aprx-2.08.svn593/coverity-build-submit.sh0000644000175000017500000000106612135460225017256 0ustar colincolin#!/bin/bash set -e export PATH=$PATH:~/src/cov-analysis-linux64-6.5.1/bin make clean ./configure rm -rf cov-int cov-build --dir cov-int make tar cvfz aprx-coverity.tgz cov-int rm -rf cov-int VERSION="`cat VERSION`" PROJECT="Aprx" PASSWORD="`cat ~/.covpw`" echo "Uploading Aprx version $VERSION to Coverity..." curl --form file=@aprx-coverity.tgz --form project="$PROJECT" \ --form password="$PASSWORD" \ --form email=oh2mqk@sral.fi \ --form version="$VERSION" \ --form description="" \ http://scan5.coverity.com/cgi-bin/upload.py rm -f aprx-coverity.tgz aprx-2.08.svn593/ax25.c0000644000175000017500000001541112305424772013410 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" /* * -- * C0 00 * 82 A0 B4 9A 88 A4 60 * 9E 90 64 90 A0 9C 72 * 9E 90 64 A4 88 A6 E0 * A4 8C 9E 9C 98 B2 61 * 03 F0 * 21 36 30 32 39 2E 35 30 4E 2F 30 32 35 30 35 2E 34 33 45 3E 20 47 43 53 2D 38 30 31 20 * C0 * -- */ int ax25_to_tnc2_fmtaddress(char *dest, const uint8_t *src, int markflag) { int i, c; int ssid; int seen_space = 0; /* 6 bytes of station callsigns in shifted ASCII format.. */ for (i = 0; i < 6; ++i, ++src) { c = (*src) & 0xFF; if (c & 1) { *dest = 0; return ~c; /* Bad address-end flag ? */ } /* Don't copy spaces or 0 bytes */ c = c >> 1; if (c == 0 || c == 0x20) { seen_space = 1; continue; } if (!seen_space && (('A' <= c && c <= 'Z') || ('0' <= c && c <= '9'))) { *dest++ = c; } else { *dest = 0; return ~c; // Bad character in callsign } } /* 7th byte carries SSID et.al. bits */ c = (*src) & 0xFF; /* (c & 1) can be non-zero - at last address! */ ssid = (c >> 1) & 0x0F; if (ssid) { /* don't print SSID==0 value */ dest += sprintf(dest, "-%d", ssid); } if ((c & 0x80) && markflag) { *dest++ = '*'; /* Has been digipeated.. */ } *dest = 0; return c; } // Return 0 on OK, != 0 on errors int parse_ax25addr(uint8_t ax25[7], const char *text, int ssidflags) { int i = 0; int ssid = 0; char c; while (i < 6) { c = *text; if (c == '-' || c == '*' || c == '\0') break; if (!(('A' <= c && c <= 'Z') || ('0' <= c && c <= '9'))) { // Valid chars: [A-Z0-9] return 1; } ax25[i] = c << 1; ++text; ++i; } while (i < 6) { ax25[i] = ' ' << 1; // they are wanted as spaces.. ++i; } ax25[6] = ssidflags; if (*text == 0) return 0; if (*text == '-') { ++text; } else if ( *text != '*' && *text != 0) { return 1; } for (; (*text != '\0') && (*text != '*') && ('0' <= *text) && (*text <= '9'); ++text) { ssid = ssid * 10 + (*text - '0'); } if (*text == '*') { ++text; ssidflags |= 0x80; // Set H-bit.. ax25[6] |= 0x80; // Set H-bit.. } if (ssid > 15 || *text != '\0') { return 1; // Bad values } ssid &= 0x0F; // Limit it to 4 bits ax25[6] = (ssid << 1) | ssidflags; return 0; } int ax25_format_to_tnc(const uint8_t *frame, const int framelen, char *tnc2buf, const int tnc2buflen, int *frameaddrlen, int *tnc2addrlen, int *is_aprs, int *ui_pid) { int i, j; const uint8_t *s = frame; const uint8_t *e = frame + framelen; char *t = tnc2buf; int viacount = 0; if (debug>1) { printf("ax25_format_to_tnc() len=%d ",framelen); hexdumpfp(stdout, frame, framelen, 1); printf("\n"); } if (framelen > sizeof(tnc2buf) - 80) { /* Too much ! Too much! */ return 0; } /* Phase 1: scan address fields. */ /* Source and Destination addresses must be printed in altered order.. */ *t = 0; i = ax25_to_tnc2_fmtaddress(t, frame + 7, 0); /* source */ t += strlen(t); *t++ = '>'; *t = 0; // end-string, just in case.. j = ax25_to_tnc2_fmtaddress(t, frame + 0, 0); /* destination */ t += strlen(t); // if (!((i & 0xE0) == 0x60 && (j & 0xE0) == 0xE0)) { // if (debug) printf("Ax25FmtToTNC2: %s SSID-bytes: %02x,%02x\n", tnc2buf, i,j); // } if (i < 0 /* || ((i & 0xE0) != 0x60)*/) { // Top 3 bits should be: 011 /* Bad format */ if (debug) printf("Ax25FmtToTNC2: Bad source address; SSID-byte=0x%02x\n",i); return 0; } if (j < 0/* || ((j & 0xE0) != 0xE0)*/) { // Top 3 bits should be: 111 /* Bad format */ if (debug) printf("Ax25FmtToTNC2: Bad destination address; SSID-byte=0x%x\n",j); return 0; } s = frame + 14; if ((i & 1) == 0) { /* addresses continue after the source! */ for (; s < e;) { *t++ = ','; /* separator char */ *t = 0; // end-string, just in case.. i = ax25_to_tnc2_fmtaddress(t, s, 1); // Top 3 bits are: H11 ( H = "has been digipeated" ) if (i < 0 /* || ((i & 0x60) != 0x60) */) { /* Bad format */ if (debug) printf("Ax25FmtToTNC2: Bad via address; addr='%s' SSID-byte=0x%x\n",t,i); return 0; } t += strlen(t); s += 7; ++ viacount; if (i & 1) break; /* last address */ } } if (viacount > 8) { if (debug) printf("Ax25FmtToTNC2: Found %d via fields, limit is 8!\n", viacount); return 0; } *frameaddrlen = s - frame; *tnc2addrlen = t - tnc2buf; /* Address completed */ if ((s + 2) >= e) // too short payload return 0; /* never happens ?? */ *t++ = ':'; /* end of address */ *t = 0; // end-string, just in case.. if (s[0] != 0x03) { // Not AX.25 UI frame *ui_pid = -1; return t - tnc2buf; /* But say that the frame is OK, and let it be possibly copied to Linux internal AX.25 network. */ } if (s[0] == 0x03 && s[1] != 0xF0) { // AX.25 UI frame, but no with APRS's PID value *ui_pid = s[1]; return t - tnc2buf; } s += 2; // Skip over Control and PID bytes *ui_pid = 0xF0; // This was previously verified /* Copy payload - stop at first LF char */ for (; s < e; ++s) { if (*s == '\n') /* Stop at first LF */ break; *t++ = *s; } *t = 0; /* Chop off possible immediately trailing CR characters */ for ( ;t > tnc2buf; --t ) { int c = t[-1]; if (c != '\r') { break; } t[-1] = 0; } *is_aprs = 1; return t - tnc2buf; } /* Convert the binary packet to TNC2 monitor text format. Return 0 if conversion fails (format errors), 1 when format is OK. */ int ax25_to_tnc2(const struct aprx_interface *aif, const char *portname, const int tncid, const int cmdbyte, const uint8_t *frame, const int framelen) { int frameaddrlen = 0; char tnc2buf[2800]; int tnc2len = 0, tnc2addrlen = 0, is_aprs = 0, ui_pid = 0; tnc2len = ax25_format_to_tnc( frame, framelen, tnc2buf, sizeof(tnc2buf), & frameaddrlen, &tnc2addrlen, & is_aprs, &ui_pid ); if (tnc2len == 0) return 0; // Bad parse result // APRS type packets are first rx-igated (and rflog()ed) #ifndef DISABLE_IGATE if (is_aprs) { igate_to_aprsis(portname, tncid, tnc2buf, tnc2addrlen, tnc2len, 0, 1); } #endif // Send to interface system to receive it.. (digipeater!) // A noop if the interface is actually NULL. interface_receive_ax25(aif, portname, is_aprs, ui_pid, frame, frameaddrlen, framelen, tnc2buf, tnc2addrlen, tnc2len); return 1; } aprx-2.08.svn593/aprx.c0000644000175000017500000003712412313325035013577 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" /* Bits used only in the main program.. */ #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_TIME_H # include #endif #include #ifdef HAVE_SYS_WAIT_H #include #endif int debug; int verbout; int erlangout; const char *rflogfile; const char *aprxlogfile; const char *mycall; float myloc_lat; float myloc_coslat; float myloc_lon; const char *myloc_latstr; const char *myloc_lonstr; const char *tocall = "APRX28"; const uint8_t tocall25[7] = {'A'<<1,'P'<<1,'R'<<1,'X'<<1,'2'<<1,'8'<<1,0x60}; #ifndef CFGFILE #define CFGFILE "/etc/aprx.conf" #endif const char *pidfile = VARRUN "/aprx.pid"; int die_now; int log_aprsis; const char *swname = "aprx"; const char *swversion = APRXVERSION; static void sig_handler(int sig) { die_now = 1; signal(sig, sig_handler); if (debug) { // Avoid stdio FILE* interlocks within signal handler char buf[64]; sprintf(buf, "SIGNAL %d - DYING!\n", sig); write(1, buf, strlen(buf)); } } static void sig_child(int sig) { int status; int pid; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { beacon_childexit(pid); } } static void usage(void) { printf("aprx: [-d[d[d]]][-e][-i][-v][-L][-l logfacility] [-f %s]\n", CFGFILE); printf(" version: %s\n", swversion); printf(" -f %s: where the configuration is\n", CFGFILE); printf(" -v: Outputs textual format of received packets, and data on STDOUT.\n"); printf(" -e: Outputs raw ERLANG-report lines on SYSLOG.\n"); printf(" -i: Keep the program foreground without debugging printouts.\n"); printf(" -l ...: sets syslog FACILITY code for erlang reports, default: LOG_DAEMON\n"); printf(" -d: turn debug printout on, use to verify config file!\n"); printf(" twice: prints also interaction with aprs-is system..\n"); printf(" -L: Log also all of APRS-IS traffic on relevant log.\n"); exit(64); /* EX_USAGE */ } static void versionprint() { printf("aprx: %s\n", swversion); exit(1); } void fd_nonblockingmode(int fd) { int __i = fcntl(fd, F_GETFL, 0); if (__i >= 0) { /* set up non-blocking I/O */ __i |= O_NONBLOCK; __i = fcntl(fd, F_SETFL, __i); } // return __i; } int time_reset = 1; // observed time jump, initially as "reset is happening!" static struct timeval old_tick; // monotonic // static struct timeval old_now; // wall-clock static int timetick_count; void timetick(void) { ++timetick_count; old_tick = tick; //old_now = now; // Monotonic (or as near as possible) clock.. // .. which is NOT wall clock time. #ifdef HAVE_CLOCK_GETTIME struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); tick.tv_usec = ts.tv_nsec/1000; tick.tv_sec = ts.tv_sec; // if (debug) printf("newtick: %d.%6d\n", tick.tv_sec, tick.tv_usec); #else gettimeofday(&tick, NULL); // fallback when no clock_gettime() is available #endif // Wall clock time // gettimeofday(&tick, NULL); // Main program clears this when appropriate int delta = 0; if (old_tick.tv_sec != 0) { delta = tv_timerdelta_millis(&old_tick, &tick); if (delta < -1) { // Up to 0.99999 seconds backwards for a leap second if (debug) { printf("MONOTONIC TIME JUMPED BACK BY %g SECONDS. ttcallcount=%d\n", delta/1000.0, timetick_count); } time_reset = 1; } else if (delta > 32000) { // 30.0 + leap second + margin if (debug) { printf("MONOTONIC TIME JUMPED FORWARD BY %g SECONDS. ttcallcount=%d mypid=%d\n", delta/1000.0, timetick_count, getpid()); } time_reset = 1; } else { // Time is OK. // time_reset = 0; } } else { time_reset = 1; // This happens before argv is parsed, thus debug is never set. // But if it sets happens afterwards... if (debug) printf("Initializing MONOTONIC time\n"); } // if (debug>1) printf("TIMETICK %ld:%6d %d delta=%d ms\n", tick.tv_sec, tick.tv_usec, timetick_count, delta); } int main(int argc, char *const argv[]) { int i; const char *cfgfile = "/etc/aprx.conf"; const char *syslog_facility = "NONE"; int foreground = 0; int millis; int can_clear_timereset; /* Init the poll(2) descriptor array */ struct aprxpolls app = APRXPOLLS_INIT; timetick(); // init global time references setvbuf(stdout, NULL, _IOLBF, BUFSIZ); setvbuf(stderr, NULL, _IOLBF, BUFSIZ); while ((i = getopt(argc, argv, "def:hiLl:vV?")) != -1) { switch (i) { case '?': case 'h': usage(); break; case 'd': ++debug; ++foreground; break; case 'e': ++erlangout; ++foreground; break; case 'i': ++foreground; break; case 'L': log_aprsis = 1; break; case 'l': syslog_facility = optarg; break; case 'v': ++verbout; ++foreground; break; case 'f': cfgfile = optarg; break; case 'V': versionprint(); break; default: break; } } interface_init(); // before any interface system and aprsis init ! erlang_init(syslog_facility); ttyreader_init(); #ifdef PF_AX25 /* PF_AX25 exists -- highly likely a Linux system ! */ netax25_init(); #endif #ifdef ENABLE_AGWPE agwpe_init(); #endif dupecheck_init(); // before aprsis_init() ! #ifndef DISABLE_IGATE aprsis_init(); #endif filter_init(); pbuf_init(); i = readconfig(cfgfile); if (i) { fflush(stdout); fprintf(stderr, "Seen configuration errors. Aborting!\n"); fflush(stderr); exit(1); // CONFIION ERRORS SEEN! ABORT! } erlang_start(1); #ifndef DISABLE_IGATE historydb_init(); #endif if (debug || verbout) { if (!mycall #ifndef DISABLE_IGATE && !aprsis_login #endif ) { fflush(stdout); fprintf(stderr, "APRX: NO GLOBAL MYCALL= PARAMETER CONFIGURED, WILL NOT CONNECT APRS-IS\n(This is OK, if no connection to APRS-IS is needed.)\n"); } else if (!mycall #ifndef DISABLE_IGATE && !aprsis_login #endif ) { fflush(stdout); fprintf(stderr, "APRX: NO GLOBAL APRSIS-LOGIN= PARAMETER CONFIGURED, WILL NOT CONNECT APRS-IS\n(This is OK, if no connection to APRS-IS is needed.)\n"); } } if (!foreground) { /* See if pidfile exists ? */ FILE *pf = fopen(pidfile, "r"); if (pf) { /* See if the pid exists ? */ int rc, er; int pid = -1; fscanf(pf, "%d", &pid); fclose(pf); if (pid > 0) { rc = kill(pid, 0); er = errno; if ((rc == 0) || (er == EPERM)) { fflush(stdout); fprintf(stderr, "APRX: PIDFILE '%s' EXISTS, AND PROCESSID %d INDICATED THERE EXISTS TOO. FURTHER INSTANCES CAN ONLY BE RUN ON FOREGROUND!\n", pidfile, pid); fflush(stderr); exit(1); } } } } if (!foreground) { int pid = fork(); if (pid > 0) { /* This is parent */ exit(0); } /* child and error cases continue on main program.. */ poll((void*)&pid, 0, 500); } if (1) { /* Open the pidfile, if you can.. */ FILE *pf = fopen(pidfile, "w"); setsid(); /* Happens or not ... */ if (!pf) { /* Could not open pidfile! */ fflush(stdout); fprintf(stderr, "COULD NOT OPEN PIDFILE: '%s'\n", pidfile); pidfile = NULL; } else { int f = fileno(pf); if (flock(f, LOCK_EX|LOCK_NB) < 0) { if (errno == EWOULDBLOCK) { printf("Could not lock pid file file %s, another process has a lock on it. Another process running - bailing out.\n", pidfile); } else { printf("Failed to lock pid file %s: %s\n", pidfile, strerror(errno)); } exit(1); } fprintf(pf, "%ld\n", (long) getpid()); // Leave it open - flock will prevent double-activation dup(f); // don't care what the fd number is fclose(pf); } } erlang_start(2); // reset PID, etc.. // Do following as late as possible.. // In all cases we close STDIN/FD=0.. // .. and replace it with reading from /dev/null.. i = open("/dev/null", O_RDONLY, 0); if (i >= 0) { dup2(i, 0); close(i); } // Leave STDOUT and STDERR open if (!foreground) { // when daemoning, we close also stdout and stderr.. dup2(0, 1); dup2(0, 2); } // .. but not latter than this. // Set default signal handling signal(SIGTERM, sig_handler); signal(SIGINT, sig_handler); signal(SIGHUP, sig_handler); signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, sig_child); // Must be after config reading ... netresolv_start(); #ifndef DISABLE_IGATE aprsis_start(); #endif #ifdef PF_AX25 /* PF_AX25 exists -- highly likely a Linux system ! */ netax25_start(); #endif #ifdef ENABLE_AGWPE agwpe_start(); #endif telemetry_start(); #ifndef DISABLE_IGATE igate_start(); #endif aprxlog("APRX start"); // The main loop can_clear_timereset = 0; while (!die_now) { timetick(); // pre-poll aprxpolls_reset(&app); tv_timeradd_millis( &app.next_timeout, &tick, 30000 ); // 30 seconds i = ttyreader_prepoll(&app); // if (debug>3)printf("after ttyreader prepoll - timeout millis=%d\n",aprxpolls_millis(&app)); #ifndef DISABLE_IGATE i = aprsis_prepoll(&app); // if (debug>3)printf("after aprsis prepoll - timeout millis=%d\n",aprxpolls_millis(&app)); #endif i = beacon_prepoll(&app); // if (debug>3)printf("after beacon prepoll - timeout millis=%d\n",aprxpolls_millis(&app)); #ifdef PF_AX25 /* PF_AX25 exists -- highly likely a Linux system ! */ i = netax25_prepoll(&app); // if (debug>3)printf("after netax25 prepoll - timeout millis=%d\n",aprxpolls_millis(&app)); #endif #ifdef ENABLE_AGWPE i = agwpe_prepoll(&app); // if (debug>3)printf("after agwpe prepoll - timeout millis=%d\n",aprxpolls_millis(&app)); #endif i = erlang_prepoll(&app); // if (debug>3)printf("after erlang prepoll - timeout millis=%d\n",aprxpolls_millis(&app)); i = telemetry_prepoll(&app); // if (debug>3)printf("after telemetry prepoll - timeout millis=%d\n",aprxpolls_millis(&app)); i = dupecheck_prepoll(&app); // if (debug>3)printf("after dupecheck prepoll - timeout millis=%d\n",aprxpolls_millis(&app)); i = digipeater_prepoll(&app); // if (debug>3)printf("after digipeater prepoll - timeout millis=%d\n",aprxpolls_millis(&app)); #ifndef DISABLE_IGATE i = historydb_prepoll(&app); // if (debug>3)printf("after historydb prepoll - timeout millis=%d\n",aprxpolls_millis(&app)); i = dprsgw_prepoll(&app); // if (debug>3)printf("after dprsgw prepoll - timeout millis=%d\n",aprxpolls_millis(&app)); #endif // All pre-polls are done if (can_clear_timereset) { // if (time_reset) { // printf("Clearing time_reset.\n"); // } time_reset = 0; } else { can_clear_timereset = 1; } // if (app.next_timeout <= now.tv_sec) // app.next_timeout = now.tv_sec + 1; // Just to be on safe side.. millis = aprxpolls_millis(&app); if (millis < 10) millis = 10; i = poll(app.polls, app.pollcount, millis); timetick(); // post-poll i = beacon_postpoll(&app); i = ttyreader_postpoll(&app); #ifdef PF_AX25 /* PF_AX25 exists -- highly likely a Linux system ! */ i = netax25_postpoll(&app); #endif #ifdef ENABLE_AGWPE i = agwpe_postpoll(&app); #endif #ifndef DISABLE_IGATE i = aprsis_postpoll(&app); #endif i = erlang_postpoll(&app); i = telemetry_postpoll(&app); i = dupecheck_postpoll(&app); i = digipeater_postpoll(&app); #ifndef DISABLE_IGATE i = historydb_postpoll(&app); i = dprsgw_postpoll(&app); #endif } aprxpolls_free(&app); // valgrind.. #ifndef DISABLE_IGATE aprsis_stop(); #endif netresolv_stop(); if (pidfile) { unlink(pidfile); } exit(0); } void printtime(char *buf, int buflen) { struct timeval tv; struct tm t; // Wall lock time for printouts gettimeofday(&tv, NULL); gmtime_r(&tv.tv_sec, &t); // strftime(timebuf, 60, "%Y-%m-%d %H:%M:%S", t); sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d.%03d", t.tm_year+1900,t.tm_mon+1,t.tm_mday, t.tm_hour,t.tm_min,t.tm_sec, (int)(tv.tv_usec / 1000)); } static struct syslog_facs { const char *name; int fac_code; } syslog_facs[] = { { "NONE", -1}, { "LOG_DAEMON", LOG_DAEMON}, #ifdef LOG_FTP { "LOG_FTP", LOG_FTP}, #endif #ifdef LOG_LPR { "LOG_LPR", LOG_LPR}, #endif #ifdef LOG_MAIL { "LOG_MAIL", LOG_MAIL}, #endif #ifdef LOG_USER { "LOG_USER", LOG_USER}, #endif #ifdef LOG_UUCP { "LOG_UUCP", LOG_UUCP}, #endif { "LOG_LOCAL0", LOG_LOCAL0}, { "LOG_LOCAL1", LOG_LOCAL1}, { "LOG_LOCAL2", LOG_LOCAL2}, { "LOG_LOCAL3", LOG_LOCAL3}, { "LOG_LOCAL4", LOG_LOCAL4}, { "LOG_LOCAL5", LOG_LOCAL5}, { "LOG_LOCAL6", LOG_LOCAL6}, { "LOG_LOCAL7", LOG_LOCAL7}, { NULL, 0} }; void aprx_syslog_init(const char *syslog_facility_name) { static int done_once = 0; int syslog_fac = LOG_DAEMON, i; if (done_once) { closelog(); /* We reconfigure from config file! */ } else ++done_once; for (i = 0;; ++i) { if (syslog_facs[i].name == NULL) { fprintf(stderr, "Sorry, unknown erlang syslog facility code name: %s, not supported in this system.\n", syslog_facility_name); fprintf(stderr, "Accepted list is:"); for (i = 0;; ++i) { if (syslog_facs[i].name == NULL) break; fprintf(stderr, " %s", syslog_facs[i].name); } fprintf(stderr, "\n"); break; } if (strcasecmp(syslog_facs[i].name, syslog_facility_name) == 0) { syslog_fac = syslog_facs[i].fac_code; break; } } if (syslog_fac >= 0) { erlangsyslog = 1; openlog("aprx", LOG_NDELAY | LOG_PID, syslog_fac); } } #ifdef HAVE_STDARG_H #ifdef __STDC__ void aprxlog(const char *fmt, ...) #else void aprxlog(fmt) #endif #else /* VARARGS */ void aprxlog(va_list) va_dcl #endif { va_list ap; char timebuf[60]; printtime(timebuf, sizeof(timebuf)); if (verbout) { #ifdef HAVE_STDARG_H va_start(ap, fmt); #else const char *fmt; va_start(ap); fmt = va_arg(ap, const char *); #endif fprintf(stdout, "%s ", timebuf); vfprintf(stdout, fmt, ap); (void)fprintf(stdout, "\n"); #ifdef HAVE_STDARG_H va_end(ap); #endif } if (aprxlogfile) { FILE *fp; #ifdef HAVE_STDARG_H va_start(ap, fmt); #else const char *fmt; va_start(ap); fmt = va_arg(ap, const char *); #endif #if defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); #endif fp = fopen(aprxlogfile, "a"); if (fp != NULL) { setlinebuf(fp); fprintf(fp, "%s ", timebuf); vfprintf(fp, fmt, ap); (void)fprintf(fp, "\n"); fclose(fp); } #if defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); #endif #ifdef HAVE_STDARG_H va_end(ap); #endif } } aprx-2.08.svn593/SVNVERSION0000644000175000017500000000000412320106140014073 0ustar colincolin593 aprx-2.08.svn593/apparmor.aprx0000644000175000017500000000045012021467263015174 0ustar colincolin#include /sbin/aprx { #include #include capability setgid, capability setuid, capability sys_chroot, /etc/aprx.conf r, owner /var/run/aprx.pid rwk, owner /var/run/aprx.state rwk, owner /var/log/aprx/* rwk, } aprx-2.08.svn593/aprx.8.in0000644000175000017500000012742212306650223014134 0ustar colincolin.TH aprx 8 "@DATEVERSION@" .LO 8 .SH NAME .B Aprx-2 \- An APRS iGate application with integrated Digipeater. .SH SYNOPSIS .B aprx .RB [ \-d [ d [ d ]]] .RB [ \-e ] .RB [ \-i ] .RB [ \-v ] .RB [ \-V ] .RB [ \-l " \fIsyslogfacilityname\fR]" .RB [ \-f " \fI@CFGFILE@\fR]" .SH DESCRIPTION The .B aprx program is a special purpose Ham-radio application supplying infrastructure services for APRS protocol use. .PP A more detailed manual is available at: .br http://ham.zmailer.org/oh2mqk/aprx/aprx-manual.pdf .PP .SH FEATURES The Aprx begun as a receive-only APRS iGate application with minimum system support technology requirements. This version has also multi-port digipeater support, transmit iGate, and experimental D-PRS-to-APRS RF/Rx-iGate. .PP .IP \(bu 3 The Aprx does not require machine to have any other software in it, than things in UNIX standard libc. In particular no special AX.25 libraries at all, nor widgets or even C++ runtime. .IP \(bu 3 Important goal has been to keep R/W memory footprint as small as possible, and on general purpose i386 Linux a single radio port iGate+digipeater is now around 250 kB of R/W memory allocations. .IP \(bu 3 Any UNIX (and UNIX like) platform should work for the Aprx, or be trivially ported. .IP \(bu 3 The Aprx can listen "TNC2 monitor" and "KISS" speaking TNCs on any serial ports. .IP \(bu 3 For Aprx the serial port can be ordinary host computer port, a USB serial port, or remote port on a remote server behind the internet, like cisco router AUX ports (port 4001, TCP STREAM without TELNET escapes.) .IP \(bu 3 The Aprx does not require machine to have AX.25 protocol support internally! (Thus this works also on e.g. Solaris and BSD machines without PF\_AX25 sockets.) .IP \(bu 3 On Linux machine with kernel internal AX.25 protocol support, the Aprx can listen on it with promiscuous mode and in order to use that, the Aprx must be started as .I root user, and be configured to list interface callsigns that APRS packets are coming in. The AX.25 socket listening is not in itself configurable, it is always exists in Linux systems, and related configuration parameters are ignored in other platforms. This socket listening does not need auxiliary "libax25" to function. .IP \(bu 3 The Aprx program can be run without root privileges at least against remote serial port servers. One must change local serial port ownership or access-groups (if any are used) to userid that runs the program and possibly do several changes of file paths in configuration file beginning with its location (startup parameter). How that is done is up to the user or system integrator of this program. .IP \(bu 3 The Aprx connects with one callsign-ssid pair to APRS-IS core for all received radio ports. .IP \(bu 3 The Aprx Rx-iGate knows that messages with following tokens in AX.25 VIA fields are not to be relayed into APRS-IS network: .RS 9 .B "RFONLY, NOGATE, TCPIP, TCPXX" .RE .IP \(bu 3 The Aprx Rx-iGate knows that following source address prefixes are bogus and thus messages with them are to be junked: .RS 9 .B "WIDE, RELAY, TRACE, TCPIP, TCPXX, NOCALL, N0CALL" .RE .IP \(bu 3 The Aprx Rx-iGate Drops all .I query messages ("?"). .IP \(bu 3 The Aprx Rx-iGate opens up all 3rd party messages ("}"), and checks the internal data if it is OK to be gated out to APRS-IS. .IP \(bu 3 The Aprx has built-in "Erlang monitor" mechanism that telemeters each receiving interface to APRS-IS. It can also syslog the interface specific channel occupancy, and optionally can output to STDOUT. .IP \(bu 3 The Aprx (since version 1.91) can do digipeater functions. .IP \(bu 3 The Aprx (since version 1.99) does have experimental D-STAR D-PRS to APRS gateway functionality. See the .I aprx-manual.pdf for details. .IP \(bu 3 The Aprx can be run on systems without writable storage, even with very little memory, like on NSLU2, and OpenWrt platforms. The experiments have shown that a single radio Tx-iGate+digipeater works with less than 300 kB of writable RAM for the Aprx itself. Additional memory is necessary for operating system services of TCP/IP networking, and serial port drivers. .PP .SH OPTIONS The .B aprx has following runtime options: .TP .B "\-i" Keep the program foreground without debugging outputs. .TP .B "\-d" Turn on verbose debugging, outputs data to STDOUT. .TP .B "\-dd" the "more debug" mode shows also details of network interaction with the APRS-IS network service. .TP .B "\-ddd" the "even more debug" mode shows also detail classification of every kind of frame received in KISS variants. .TP .B "\-e" .I "Erlang output" prints 10 minute and 60 minute traffic accumulation byte counts, and guestimates on channel occupancy, alias "Erlang". These outputs are sent to STDOUT, which system operator may choose to log elsewere. This is independent if the "\-l" option below. .TP .BI "\-f " "@CFGFILE@" Configuration file, given path is built-in default, and can be overridden by the program runner. .TP .BR "\-l" " \fIsyslogfacilityname\fR" Defines .RB syslog (3) facility code used by the erlang reporter by defining its name. Default value is: .BR NONE , and accepted values are: .BR LOG_DAEMON , .BR LOG_FTP , .BR LOG_LPR , .BR LOG_MAIL , .BR LOG_NEWS , .BR LOG_USER , .BR LOG_UUCP , .BR LOG_LOCAL0 , .BR LOG_LOCAL1 , .BR LOG_LOCAL2 , .BR LOG_LOCAL3 , .BR LOG_LOCAL4 , .BR LOG_LOCAL5 , .BR LOG_LOCAL6 , .BR LOG_LOCAL7 . That list is subject to actual facility code set in the system, and in any case if you specify a code that is not known, then the program will complain during the startup, and report it. This is independent of the "\-e" option above. .TP .B "\-v" Verbose logging of received traffic to STDOUT. Lines begin with reception timestamp (UNIX time_t seconds), then TAB, and either data as is, or with prefix byte: "*" for "discarded due to data content", or possibly "#" for "discarded due to APRS-IS being unreachable". .TP .B "\-V" Print source version compiled to this binary, and exit. .PP .SS DEBUGGING SYSTEM Use parameter set .B "\-ddv" (or .BR "\-dddv" ) to test new configuration by running it synchronously to console. .PP .SS NORMAL OPERATION Running the .B aprx program without any of option flags: .BR "\-d" , .BR "\-v" ", or" .B "\-e" reads possibly given configuration, then automatically backgrounds the process, and writes .IR pidfile . When the process whose number written in .I pidfile is then sent a SIGTERM signal, it automatically shuts down itself, and removes the .IR pidfile . The .I pidfile can be runtime configured with the .BI \-f " @CFGFILE@" file, and it has default name of: .IR "@VARRUN@/aprx.pid" . .PP .SH CONFIGURATION FILE The configuration file is used to setup the program to do its job. .PP You can construct following configurations: .PP .IP \(bu 3 A .I receive-only iGate server. .IP \(bu 3 A digipeater with bi-directional iGate server. .IP \(bu 3 A .I "single radio" digipeater. (The most common type of digipeater.) .IP \(bu 3 A .I multi-interfaced digipeater relaying traffic in between multiple radios. (On same or on separate frequencies.) .IP \(bu 3 A .I "viscuous digipeater," which relays a packet it heard from viscuous source after the viscuous delay, .I unless it was heard more times than only once, or it was heard from non-viscuous source before the viscuous one was digipeated. This allows of making fill-in digipeaters that will not digipeat the packet, if that same packet was heard twice or more before the viscuos delay expired. .PP In the configuration file a line ending backslash (\\) character concatenates next input line into itself. Combined result can be up to 8000 bytes long. This combination can be a bit surprising: .RS 3em .nf \fC#beacon .... long text \\ continuation\fR .fi .RE results in single long input line that begins with '#' (it is comment) and all continuations following it have been folded in. Presented line number of combined continuation is the line number of the .I last line segment in this type of multi-line input. .PP In the configuration file there is special treatment for quoted strings. They are stripped of the outer quotes, and "\fC\\\fR" character is processed within the source string to produce an output string. The escapes are: .TP .B "\fC\\\\n" Produces newline character (Control-J) on the output string. .TP .B "\fC\\\\r" Produces carriage return character (Control-M) on the output string. .TP .B "\fC\\\\\\\\" Places a back-slash on the output string. .TP .B "\fC\\\\"" .\" foo " Places a double-quote on the output string. .TP .B "\fC\\\\'" Places a single-quote on the output string. .TP .B "\fC\\\\xHH" Lower-case "x" precedes two hex digits which ensemble is then converted to a single byte in the output string. .PP The complex encodings are for possible initstrings of the external devices, and in particular for initstrings even a nul byte ( \\x00 ) is supported. .PP A configuration token without surrounding quotes does not understand the backslash escapes. .PP .nf \fC # # Sample configuration file for the APRX -- an Rx-only APRS iGate with # Digipeater functionality. # # # Simple sample configuration file for the APRX-2 # # This configuration is structured with Apache HTTPD style tags # which then contain subsystem parameters. # # # For simple case, you need to adjust 4 things: # - Mycall parameter # - Select correct type of interface (ax25-device or serial-device) # - Optionally set a beacon telling where this system is # - Optionally enable digipeater with or without tx-igate # # # # Define the parameters in following order: # 1) ** zero to many # 2) ** zero or one # 3) ** one to many # 4) ** zero to many # 5) ** zero to many (at most one for each Tx) # # # Global macro for simplified callsign definition: # Usable for 99+% of cases. # mycall N0CALL-1 # # Global macro for simplified "my location" definition in # place of explicit "lat nn lon mm" at beacons. Will also # give "my location" reference for "filter m/100". # #myloc lat ddmm.mmN lon dddmm.mmE # The login parameter: # Station call\-id used for relaying APRS frames into APRS\-IS. # Use this only to define other callsign for APRS\-IS login. # #login OTHERCALL-7 # login defaults to $mycall # # The passcode parameter: # Unique code for your callsign to allow transmitting packets # into the APRS-IS. # passcode -1 # APRS-IS server name and portnumber. # Every reconnect does re\-resolve the name to IP address. # Some alternates are shown below, choose something local to you. # server rotate.aprs2.net 14580 #server noam.aprs2.net 14580 #server soam.aprs2.net 14580 #server euro.aprs2.net 14580 #server asia.aprs2.net 14580 #server aunz.aprs2.net 14580 # Some APRS\-IS servers tell every about 20 seconds to all contact # ports that they are there and alive. Others are just silent. # Recommended value 3*"heartbeat" + some \-> 120 (seconds) # #heartbeat\-timeout 0 # Disabler of heartbeat timeout # APRS-IS server may support some filter commands. # See: http://www.aprs-is.net/javAPRSFilter.aspx # # You can define the filter as single long quoted string, or as # many short segments with explaining comments following them. # # Usability of these filters for a Tx-iGate is dubious, but # they exist in case you for example want to Tx-iGate packets # from some source callsigns in all cases even when they are # not in your local area. # #filter "possibly multiple filter specs in quotes" # #filter "m/100" # My-Range filter #filter "f/OH2XYZ\-3/50" # Friend-Range filter # pidfile is UNIX way to tell that others that this program is # running with given process-id number. This has compiled-in # default value of: pidfile @VARRUN@/aprx.pid # #pidfile @VARRUN@/aprx.pid # rflog defines a rotatable file into which all RF-received packets # are logged. # #rflog @VARLOG@/aprx\-rf.log # aprxlog defines a rotatable file into which most important # events on APRS\-IS connection are logged, namely connects and # disconnects. # #aprxlog @VARLOG@/aprx.log # erlangfile defines a mmap():able binary file, which stores # running sums of interfaces upon which the channel erlang # estimator runs, and collects data. # Depending on the system, it may be running on a filesystem # that actually retains data over reboots, or it may not. # With this backing store, the system does not loose cumulating # erlang data over the current period, if the restart is quick, # and does not stradle any exact minute. # (Do restarts at 15 seconds over an even minute..) # This file is around 0.7 MB per each interface talking APRS. # If this file is not defined and can not be created, # internal non-persistent in-memory storage will be used. # # Built-in default value is: @VARRUN@/aprx.state # #erlangfile @VARRUN@/aprx.state # erlang\-loglevel is config file edition of the "\-l" option # pushing erlang data to syslog(3). # Valid values are (possibly) following: NONE, LOG_DAEMON, # LOG_FTP, LOG_LPR, LOG_MAIL, LOG_NEWS, LOG_USER, LOG_UUCP, # LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, # LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7. If the parameter value is # not acceptable, list of accepted values are printed at startup. # #erlang\-loglevel NONE # erlanglog defines a rotatable file into which erlang data # is written in text form. # #erlanglog @VARLOG@/erlang.log # erlang\-log1min option logs to syslog/file also 1 minute # interval data from the program. (In addition to 10m and 60m.) # #erlang\-log1min # *********** Multiple definitions can follow ********* # ax25\-device Lists AX.25 ports by their callsigns that in Linux # systems receive APRS packets. If none are defined, # or the system is not Linux, the AX.25 network receiver # is not enabled. Used technologies need at least # Linux kernel 2.4.x # # tx\-ok Boolean telling if this device is able to transmit. # # # ax25\-device $mycall # Either $mycall macro, or actual callsign # #tx\-ok false # transmitter enable defaults to false # #telem\-to\-is true # set to false to disable # # The TNC serial options. Parameters are: # \- /dev/ttyUSB1 \-\- tty device # \- 19200 \-\- baud rate, supported ones are: # 1200, 2400, 4800, 9600, 19200, 38400, ... # \- 8n1 \-\- 8\-bits, no parity, one stop\-bit, # no other supported modes # \- "KISS" \- plain basic KISS mode # \- "XORSUM" alias "BPQCRC" \- KISS with BPQ "CRC" byte # \- "SMACK" alias "CRC16" \- KISS with real CRC # \- "FLEXNET" \- KISS with real CRC # \- "TNC2" \- TNC2 monitor format # \- "DPRS" \- DPRS (rx) Gateway # # # serial\-device /dev/ttyUSB0 19200 8n1 KISS # #callsign $mycall # Either $mycall macro, or actual callsign # #tx\-ok false # transmitter enable defaults to false # #telem\-to\-is true # set to false to disable # # # # serial\-device /dev/ttyUSB1 19200 8n1 TNC2 # #callsign $mycall # Either $mycall macro, or actual callsign # #tx\-ok false # TNC2 monitor can not have transmitter # #telem\-to\-is true # set to false to disable # # # # serial\-device /dev/ttyUSB1 19200 8n1 DPRS # callsign dprsgwcallsign # must define actual callsign # #tx\-ok false # DPRS monitor can not do transmit # #telem\-to\-is true # set to false to disable # # # *********** Multiple definitions can follow ********* # # Beacons are sent out to radio transmitters AND/OR APRSIS. # Default is "both", other modes are settable. # #beaconmode { aprsis | both | radio } # # Beacons are sent from a circullar transmission queue, total cycle time # of that queue is 20 minutes by default, and beacons are "evenly" # distributed along it. Actual intervals are randomized to be anything # in between 80% and 100% of the cycle-size / number-of-beacons. # First beacon is sent out 30 seconds after system start. # Tune the cycle-size to be suitable to your number of defined beacons. # #cycle-size 20m # # # Basic beaconed thing is positional message of type "!": # #beacon symbol "R&" lat "0000.00N" lon "00000.00E" comment "Rx-only iGate" #beacon symbol "R&" $myloc comment "Rx-only iGate" # # Following are basic options: # 'symbol' no default, must be defined! # 'lat' coordinate latitude: ddmm.mmN (no default!) # 'lon' coordinate longitude: dddmm.mmE (no default!) # '$myloc' coordinate values taken from global 'myloc' entry, # and usable in place of explicit 'lat'+'lon'. # 'comment' optional tail part of the item, default is nothing # # Sample symbols: # R& is for "Rx-only iGate" # I& is for "Tx-iGate" # /# is for "Digipeater" # I# is for "Tx-iGate + Digipeater" # # Additional options are: # 'srccall' parameter sets claimed origination address. # 'dstcall' sets destination address, default "APRXnn" # 'interface' parameter picks an interface (must be "tx-ok true" type) # 'via' sets radio distribution pattern, default: none. # 'timefix' On APRS messages with HMS timestamp (hour:min:sec), the # system fixes appropriate field with transmit time timestamp. # # Message type is by default '!', which is positional no timestamp format. # Other possible formats are definable with options: # 'type' Single character setting type: ! = / @ # 'item' Defines a name of Item (')') type beacons. # 'object' Defines a name of Object (';') type beacons. # # 'file' option tells a file at which a _raw_ APRS message content is # expected to be found as first line of text. Line ending newline # is removed, and no escapes are supported. The timefix is # available, though probably should not be used. # # 'exec' option defines program path for a program whose stdout is # read up to first newline (which must be present), and then # transmit as beacon content. No format helpers are supplied, # although 'timefix' can be used. # 'timeout' option is associated with 'exec', and defines when the # exec must by latest produce the output, or the subprogram # execution is killed. Default value is 10 seconds. # # The parameter sets can vary: # a) 'srccall nnn-n dstcall "string" symbol "R&" lat "ddmm.mmN" lon "dddmm.mmE" [comment "any text"] # b) 'srccall nnn-n dstcall "string" raw "string"' # # The a) form flags on some of possible syntax errors in parameters. # It will also create only "!" type messages. The dest parameter # defaults to "APRS", but can be used to give other destinations. # The via parameter can be used to add other keywords, like "NOGATE". # # Writing correct RAW format beacon message is very hard, # which is evidenced by the frequency of bad syntax texts # people so often put there... If you can not be persuaded # not to do it, then at least VERIFY the beacon result on # web service like findu.com, or aprs.fi # #beacon file /tmp/wxbeacon.txt #beacon srccall N0CALL\-3 raw "!0000.00NR00000.00E&aprx \- an Rx\-only iGate" #beacon srccall N0CALL\-3 raw "!0000.00NI00000.00E&aprx \- an iGate" #beacon srccall $mycall symbol "R&" lat "0000.00N" lon "00000.00E" \\ comment "aprx \- an Rx\-only iGate" #beacon srccall $mycall symbol "I&" lat "0000.00N" lon "00000.00E" \\ comment "aprx iGate" # *********** definition(s) follow ********* # # The system will always send telemetry for all of its interfaces # to APRSIS, but there is an option to define telemetry to be sent # to radio channel by using following sections for each transmitter # that is wanted to send out the telemetry. # # transmitter - callsign referring to # via - optional via-path, only 1 callsign! # source - one or more of callsigns for which # the telemetry transmission is wanted for # # # transmitter $mycall # via TRACE1-1 # source $mycall # # *********** definition(s) follow ********* # # The digipeater definitions tell transmitters that receive # AX.25 packets from possibly multiple sources, and then what # to do on the AX.25 headers of those messages. # # There is one transmitter per digipeater \-\- and inversely, there # can be at most one digipeater for each transmitter. # # In each digipeater there is at least one , usually same # as the transmitter. # # # transmitter $mycall # #ratelimit 60 120 # default: average 60 packets/minute, # # burst max 120 packets/minute # #srcratelimit 10 20 # Example: by sourcecall: # # average 10 packets/minute, # # burst max 20 packets/minute # # # source $mycall # # ratelimit 60 120 # default: average 60 packets/minute, # # # burst max 120 packets/minute # # viscous\-delay 0 # no viscous delay for RF\->RF digipeat # # ratelimit 120 # default: max 120 packets/minute # # # # # Adding APRSIS source makes this tx-igate # # source APRSIS # # ratelimit 60 120 # default: average 60 packets/minute, # # # burst max 120 packets/minute # # relay\-type third\-party # Must define this for APRSIS source! # # viscous\-delay 5 # Recommendation: 5 seconds delay to give # # # RF delivery time make itself known. # # filter t/m # Tx-iGate only messages sent to me by APRSIS # # # # \fR .fi .PP .SH GLOBAL MYCALL PARAMETER In majority of usage models, system needs single configured callsign. This is set by using the .B mycall configuration option, and latter referred to in configurations as .B $mycall parameter in place of callsigns. .PP .SH GLOBAL MYLOC PARAMETER Usually multiple beacons, and simple filter rules are wanted to be using same reference coordinate for this system. This is set by using the .B myloc configuration option, and latter referred to in configurations as .B $myloc parameter in place of "lat nn lon mm" coordinate pair of beacons. .SH APRSIS SECTION FOR APRSIS CONNECTIVITY Settings in the .B section define connectivity with the APRS-IS network service. .PP Necessary option is .IR server , and others are optional. .PP Available options are: .IP "\fClogin $mycall\fR" 8em The APRSIS network login. Defaults to the .B mycall configuration entry. .IP "\fCpasscode -1\fR" 8em Defining a small integer in range of 0 to 32767 authenticating your login to APRS-IS server. Ask for assistance from your APRS-IS managers, or calculate it yourself with .I aprspass program. (Web search engines do find several of them.) .IP "\fCserver \fIserver-name 14850\fR" 8em Define which APRS-IS is being connected to. Multiple definitions are used in round-robin style, if the connection with the previous one fails for some reason. .PP .IP "\fCfilter \fI'filter specs in quotes'\fC # \fIcomments" 8em Set filter adjunct definitions on APRS-IS server. Multiple entries are catenated together in entry order, when connecting to the server. .PP .SH LOGGING SECTION The .B section defines miscellaneous file names and options for state tracking and logging use. .PP .IP "\fCpidfile \fI@VARRUN@/aprx.pid\fR" 8em The pidfile is UNIX way to tell that others that this program is running with given process-id number. This has compiled-in default value of: \fCpidfile @VARRUN@/aprx.pid .IP "\fCrflog \fI@VARLOG@/aprx\-rf.log\fR" 8em The .I rflog defines a rotatable file into which all RF-received packets are logged. There is no default. .IP "\fCaprxlog \fI@VARLOG@/aprx.log\fR" 8em The .I aprxlog defines a rotatable file into which most important events on APRS-IS connection are logged, namely connects and disconnects. There is no default. .IP "\fCerlangfile \fI@VARRUN@/aprx.state\fR" 8em The .I erlangfile defines a mmap():able binary file, which stores running sums of interfaces upon which the channel erlang estimator runs, and collects data. Depending on the system, it may be running on a filesystem that actually retains data over reboots, or it may not. With this backing store, the system does not loose cumulating erlang data over the current period, if the restart is quick, and does not stradle any exact minute. This file is around 0.7 MB per each interface talking APRS. If this file is not defined and can not be created, internal non-persistent in-memory storage will be used. Built-in default value is: @VARRUN@/aprx.state .IP "\fCerlang\-loglevel \fINONE\fR" 8em The .I erlang\-loglevel is config file edition of the "\-l" option pushing erlang data to .IR syslog (3). Valid values are (possibly) following: NONE, LOG_DAEMON, LOG_FTP, LOG_LPR, LOG_MAIL, LOG_NEWS, LOG_USER, LOG_UUCP, LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7. If the parameter value is not acceptable, list of accepted values are printed at startup. .IP "\fCerlanglog \fI@VARLOG@/erlang.log\fR" 8em The erlanglog defines a rotatable file into which erlang data is written in text form. There is no default. .IP "\fCerlang\-log1min\fR" 8em The .I erlang\-log1min option logs to syslog/file also 1 minute interval data from the program. (In addition to 10m and 60m.) Default is off. .PP .SH INTERFACE SECTIONS FOR RADIO PORTS The .B sections define connections to radio modems. Several different styles are available: .IP \(bu 2 Local serial ports in the machine .RB ( "device\-serial /dev/ttyS0 " "\fIspeed encapsulation\fR)" .IP \(bu 2 Local USB serial ports in the machine .RB ( "device\-serial /dev/ttyUSB0 " "\fIspeed encapsulation\fR)" .IP \(bu 2 Remote served serial ports over a TCP stream. Implemented to talk with Cisco AUX ports on "range 4000" (TCP STREAM, no TELNET escapes) .RB ( "tcp\-device 12.34.56.78 4001 " "\fIencapsulation\fR)" .IP \(bu 2 Linux internal AX.25 network attached devices .RB ( "ax25\-device CALLSIGN\-1" ) are only available when running on a Linux system. On a non-Linux system it connects to a null interface, never getting anything and can always sink everything. .PP The serial port name tells what kind of port is in question, and while port baud-rate (9600) and character settings (8n1) must always be set, they are ignored for the remote connection. .PP Following .I speed modes are available: .br .B " " 1200, .I 1800, .B 2400, 4800, 9600, 19200, .I 38400, 57600, .br .I " " 115200, 230400, 460800, 500000, 576000 .br Likely available speeds are in bold, other supported values are listed in italics. .PP Following .I encapsulation modes are available: .TP 10em .B TNC2 is capable only to monitor the packets reported by TNC2 type debug output, and Rx-iGate, but they are not acceptable as source for a . .TP 10em .B DPRS is special mode for gateway from D-STAR D-PRS to APRS. This must always have a callsign definition for the gateway. .TP 10em .B KISS Basic KISS encapsulation. No checksums. Will autodetect (sometimes) packets with SMACK or FLEXNET characteristics. .TP 10em .B SMACK .IR "Stuttgart Modified Amateurradio-CRC-KISS" , which runs CRC-16 checksum on KISS datastream much in the same way as HDLC has CCITT-CRC checksum on it. .TP 10em .B FLEXNET .IR "FLEXNET" which runs a CRC checksum of its own polynomial on KISS datastream much in the same way as HDLC has CCITT-CRC checksum on it. .TP 10em .B BPQCRC XOR "checksum" on dataframes. Also known as "XKISS", and "XORSUM". This detects single bit failure, but weakly any multibit failures. Extra 0x00 bytes have no effect on checksum, etc. .PP On .BI "" sub-options the parameter is .IR tncid , which sets up KISS multiplexer parameter so that subsequent options applies only on designated KISS sub-port. .PP The .I callsign option sets port specific callsign when relaying to APRS-IS. .PP The .I "telem\-to\-is true" option can be used to disable (by explicitly setting it to 'false') radio interface telemetry transmission to APRS-IS. By default it is on. This is separate from sections, which send telemetry to RF interfaces. .PP .nf \fC serial\-device /dev/ttyUSB1 19200 8n1 KISS tx\-ok false # receive only (default) callsign OH2XYZ\-R2 # KISS subif 0 initstring "...." # initstring option timeout 900 # 900 seconds of no Rx serial\-device /dev/ttyUSB1 19200 8n1 SMACK tx\-ok false # receive only (default) callsign OH2XYZ\-R2 # KISS subif 0 initstring "...." # initstring option timeout 900 # 900 seconds of no Rx serial\-device /dev/ttyUSB2 19200 8n1 KISS initstring "...." timeout 900 # 900 seconds of no Rx callsign OH2XYZ\-2 tx\-ok true # This is our transmitter callsign OH2XYZ\-R3 # This is receiver tx\-ok false # receive only (default) tcp\-device 172.168.1.1 4001 KISS tx\-ok false # receive only (default) callsign OH2XYZ\-R4 # KISS subif 0 initstring "...." # initstring option timeout 900 # 900 seconds of no Rx ax25\-device OH2XYZ\-6 # Works only on Linux systems tx\-ok true # This is also transmitter # \fBRX-IGATE ONLY, NOT USABLE AS DIGIPEATER SOURCE\fR\fC serial\-device /dev/ttyUSB1 19200 8n1 TNC2 callsign OH2XYZ\-R6 # TNC2 has no sub-ports initstring "...." # initstring option timeout 900 # 900 seconds of no Rx .fi .SH BEACON DEFINITIONS The beacons are defined using .B configuration sections. .PP Because classical beacon definitions are highly error\-prone, this program has a new way to define them: .IP \(bu 2 The new way to define beacons: .nf \fCbeacon symbol "R&" lat "0000.00N" lon "00000.00E" \\\fR \fC comment "aprx \- iGate" \fR .fi .IP \(bu 2 Semi-clasical definition of raw APRS packet: .nf \fCbeacon raw "!0000.00NR00000.00E&aprx \- iGate"\fR .fi .IP \(bu 2 Load beacon text from a file, path data is configurable: .nf \fCbeacon file \fR\fI/path/to/file\fR .fi .IP \(bu 2 Run a program to produce beacon data in raw format: .nf \fCbeacon exec \fR\fI/path/to/file\fR\fC timeout \fR\fI10\fR .fi .PP The fields and parameters: .TP 12em .B interface An .I optional "interface" parameter tells that this beacon shall be sent only to interface whose callsign is named. Default is to send to all interfaces that have "tx\-ok true" setting. .TP 12em .B type An .I optional one character string parameter, with one of following contents: "!", "=", "/", "@", ";" and ")". .TP 12em .B srccall An .I optional "srccall" parameter tells callsign which is claimed as this particular beacon source. It must be valid AX.25 callsign in text format. When this "srccall" parameter is not given, value of "mycall" configuration entry is used. .TP 12em .B dstcall An .I optional "dstcall" parameter has built-in software version dependent value, but it can be used to define another value. .TP 12em .B via An .I optional "via" parameter defaults to nothing, but can be used to define additional "VIA" path tokens, for example: "WIDE1\-1". .TP 12em .B item An .I optional "item" parameter is for defining a name for an item type APRS packet. .TP 12em .B object An .I optional "object" parameter is for defining a name for an object type APRS packet. .TP 12em .B symbol A .I mandatory "symbol" parameter is two character code, which for Rx-only iGate is pair: "R&" .TP 12em .B lat This .I mandatory parameter defines .I latitude coordinate (that is: north/south.) It is expected to be of format: "ddmm.mmN" where "dd" defines .I two digits of .I degrees of latitude, and "mm.mm" defines two digits + decimal dot + two digits of .I minutes of latitude. Then comes literal "N" or "S" indicating hemisphere. .TP 12em .B lon This .I mandatory parameter defines .I longitude coordinate (that is: east/west.) It is expected to be of format: "dddmm.mmE" where "ddd" defines .I three digits of .I degrees of longitude, and "mm.mm" defines two digits + decimal dot + two digits of .I minutes of longitude. Then comes literal "E" or "W" indicating hemisphere. .TP 12em .B comment This .I optional parameter defines commentary text tail on the beacon packet. If you need characters outside US-ASCII character set, use of UTF-8 encoded UNICODE character set is recommended. .TP 12em .B raw This .I alternate format defines whole APRS packet content in raw text format. .I Currently this type of packets are not validated for syntax at all! .TP 12em .B file This .I alternative way defines path to a file with single text line defining content of .I raw message data. .TP 12em .B exec This .I alternative mode runs designated program, and waits for at most a .I timeout number of seconds (default 10) for the program to produce the result. .TP 12em .B timeout This is optional parameter for .I exec allowing altered timeout (number of seconds) for waiting the program to respond. Default is 10 seconds. .PP The type/symbol/lat/lon/comment-format supports only a few types of APRS packets. It splits input into small slices that are possible to validate in detail. (See "DEBUGGING SYSTEM" above.) .PP .SH RF-TELEMETRY The .I aprx system will always send telemetry for all of its interfaces to APRSIS, but there is an option to define telemetry to be sent to radio channel by using following sections for each transmitter that is wanted to send out the telemetry. .PP The parameters of .B configuration section are: .TP 12em .B transmitter A mandatory callsign referring to an .I interface. .TP 12em .B via An optional .I via-path parameter. Only 1 callsign! .TP 12em .B source One or more of .I interface callsigns for which the telemetry transmission is made. .SH DIGIPEATER The .I aprx is possible to configure as a AX.25 digipeater with APRS twists. This is done with .B configuration section and its subsections. .PP .I There can be at most one definition per each .I transmit capable interface in the system. .I On a system with multiple transmitters, this means there can .I be multiple digipeaters, each with different behaviour rules. .PP Minimalistic setup for a digipeater will be as follows: .PP .nf \fC transmitter $mycall source $mycall \fR .fi .PP In minimalistic approach the system does digipeating of packets heard on the .I $mycall interface back to same interface. Single requirement is that the .I block has .I "tx\-ok true" setting on it. .PP In more complicated approaches it is possible to define multiple sources for packets: .IP \(bu 3 Multiple device ports. .IP \(bu 3 APRSIS pseudoport, which creates the Tx-iGate functionality. .PP .SS options Main-level options are: .PP .IP \(bu 3 .I transmitter defines which interface the digipeater will output to. .IP \(bu 3 .IR " and " sub-options are explained below. .IP \(bu 3 .I sub-option is explained below. .PP .SS and sub-options The .I sub-option has priority over the .I sub-option, otherwise they are configured the same way. .PP The .I sub-option defines which AX.25 address contained keywords are treated with APRS "New-N paradigm" rules in a way where each processing node always marks its transmitter callsign on the transmitted AX.25 packet address header. .PP The .I sub-option defines which AX.25 address contained keywords are treated with APRS "New-N paradigm" rules in a way where processing node does not mark its transmitter callsign on the transmitted AX.25 packet address header. .PP Available parameters are: .TP 9em .B keys A string of comma-separated set of string tokens: .br \fC keys "TRACE,WIDE"\fR .br Alternative form for this entry is: .br \fC keys "TRACE"\fR .br \fC keys "WIDE"\fR .TP 9em .B maxdone Defines maximum number of redistribution hops that these keywords can have completed when reaching here. If accounting finds more done, the system will just drop the packet instead of digipeating it onwards. .TP 9em .B maxreq Defines maximum number of redistribution hops that these keywords can define. If accounting finds more requested, the system will just drop the packet instead of digipeating it onwards. .PP .SS sub-options Primary definer option is .B source which gives callsign of an .I from which the AX.25 packets are received for this .I block. .PP Available .B relay\-type modes on definitions are: .TP 14em .B digipeater Normal AX.25 digipeater behaviour with APRS New-N paradigm support. This is default mode. .TP 14em .B directonly Digipeat only directly heard packets. Useful for systems that are designated as "fill-in". See also "viscous\-delay". .TP 14em .B third\-party Special mode for Tx-iGate. .PP The .B ratelimit defines two parameters: .I average and .I limit number of packets sent in 60 seconds. Its definitions can be both in .I and in digipeater's .I sections, and therefore you can limit each individual source to a max accepted rate as well as define separate rate limits for the transmitter. .PP The .B viscous\-delay defines a number of seconds from 0 (default) maximum of 9 that the source will put the message on duplicate detector delay processing. All occurrances of same packet per duplicate detector during that time will be accounted on duplicate detection, and if at the end of the delay period there are more than one hit, the packet is discarded. Use delay of 0 seconds for normal digipeater, 5 seconds for a fill-in, or a Tx-iGate. .PP A javAPRSSrvr filter-adjunct style rules are possible with the .B filter options. When you want multiple filters, use multiple options with associated parameters: .nf \fC filter t/m # APRS messaging type packets filter a/la/lo/la/lo # APRS positional packets within this area \fR .fi .LP Also negative filters are possible (prefixed with minus character), which upon match cause rejection of the packet. Filters are evaluated in definition order, and first matching one will terminate the evaluation. When no filters are defined, everything is passed thru. When any filter is defined, only those matching non-negative filters are passed thru, and no default "pass everything else" behaviour exists. .LP Supported "adjunct filters" are following: .TP 8em .B A/latN/lonW/latS/lonE Area filter, defined as area enclosing within latS/latN and lonW/lonE. Latitude and longitude are entered as degrees and decimals. .TP 8em .B B/call1/call2... Budlist filter. Supports *-wildcards. .TP 8em .B D/digi1/digi2... .I Not supported at APRX internal filters .TP 8em .B E/call1/call2/... .I Not supported at APRX internal filters .TP 8em .B F/call/dist_km Great-circle distance in kilometers from friend's coordinates. No wildcarding. .br .I (TODO: check that it really works!) .TP 8em .B M/dist The .I "range around my location" filter requires that you have defined also the "myloc" configuration entry. It defines acceptance of positions and messages with senders within .I dist kilometers of the "myloc" position. .TP 8em .B O/object1/obj2... Object name filter. Supports *-wildcards. .TP 8em .B P/aa/bb/cc... Prefix filter. .TP 8em .B Q/con/ana .I The Q-construct filter is not supported. .TP 8em .B R/lat/lon/dist Range filter. Latitude and longitude are in degrees and decimals. Distance is in kilometers. No wildcards. .TP 8em .B S/pri/alt/over Symbol filter .TP 8em .B T/..../call/km .RS Type filter. Couple possible usages: .IP "\fC -t/c\fR" 22em Everything except CWOP .IP "\fC t/*/OH2RDY/50\fR" 22em Everything within 50 km of OH2RDY's last known position .PP Type code characters are: .TP 3em .B * An "all" wild-card. .TP 3em .B C A CWOP. .TP 3em .B I An ITEM. .TP 3em .B M A MESSAGE. .TP 3em .B N A NWS message. .TP 3em .B O An OBJECT. .TP 3em .B Q A QUERY. .TP 3em .B S A STATUS response. .TP 3em .B T A TELEMETRY packet or parameter message. .TP 3em .B U A USERDEF message. .TP 3em .B W A WX data packet .RE .TP 8em .B U/unproto1/unproto2... Filters by value in destination address field, supports wildcard. .PP The .B and .B sub-options exist also within each . Where such occur, the specific sub-option trumps the definition on level, and same with sub-options. This allows things like overriding flooding control keywords on source basis, should such be necessary. .PP A set of .B regex\-filter rules can be used to reject packets that are not of approved kind. Available syntax is: .IP "regex\-filter source RE" source address field .IP "regex\-filter destination RE" destination address field .IP "regex\-filter via RE" any via path field .IP "regex\-filter data RE" payload content .PP The regex\-filter exists as ad-hoc method when all else fails. .PP .SH NOTES: ERLANG The .I Erlang is telecom measurement of channel occupancy, and in this application sense it does tell how much traffic there is on the radio channel. .PP Most radio transmitters are not aware of all transmitters on channel, and thus there can happen a collision causing loss of both messages. The higher the channel activity, the more likely that collision is. For further details, refer to statistical mathematics books, or perhaps on Wikipedia. .PP In order to measure channel activity, the .B aprx program suite has these built-in statistics counter and summary estimators. .PP The .I Erlag value that the estimators present are likely somewhat .I underestimating the true channel occupancy simply because it calculates estimate of channel bit transmit rate, and thus a per-minute character capacity. It does not know true frequency of bit-stuffing events of the HDLC framing, nor each transmitter pre- and port frame PTT times. The transmitters need to stabilize their transmit oscillators in many cases, which may take up to around 500 ms! The counters are not aware of this preamble-, nor postamble-times. .PP The HDLC bit stuffing ratio is guessed to be 1:1.025 (1 extra bit every 5 bytes) .SH NOTES: PROGRAM NAME Initially this program had name .IR aprsg-ng , which was too close to another (a less low-tech C++ approach) program had. .SH BUGS/WARTS The .IR Erlang -monitor mechanisms are of rudimentary quality, and can seriously underestimate the channel occupancy by ignoring pre- and postample transmissions, which can be as high as 50 centiseconds for preample, and 20 centiseconds for postample! When entire packet takes 50 centiseconds, such preample alone doubles channel occupancy. A 6pack protocol on serial link (instead of KISS) could inform receiver better on carrier presense times, however even that underestimates RF power presense (RSSI) signal. (6pack is not supported.) .PP On serial lines supports really only 8n1 mode, not at all like: 7e1. On the other hand, there really is no sensible usage for anything but 8n1... .SH SEE ALSO Couple web sites: .br .IR "http://www.aprs2.net/" , .br .IR "http://www.aprs-is.net/" , .br .IR "http://wiki.ham.fi/Aprx.en" , .br .I "http://ham.zmailer.org/oh2mqk/aprx/aprx-manual.pdf" .PP .BR aprx-stat (8) .SH AUTHOR This little piece was written by .I "Matti Aarnio, OH2MQK" during a dark and rainy fall and winter of 2007-2008 after a number of discussions grumbling about current breed of available software for APRS iGate use in Linux (or of any UNIX) platforms. Fall and winter 2009-2010 saw appearance of digipeater functionality. .PP Principal contributors and test users include: .IR "Pentti Gronlund, OH3BK" , .IR "Reijo Hakala, OH1GWK" . Debian packaging by .IR "Kimmo Jukarinen, OH3GNU" . Testing of SMACK variant of KISS by .IR "Patrick Hertenstein, DL1GHN" . The beacon exec functionality prototype by .IR "Kamil Palkowiski SQ8KFH" . aprx-2.08.svn593/agwpesocket.c0000644000175000017500000004724712314016324015147 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" #ifdef ENABLE_AGWPE /*** AGWPE interface description from Xastir + AGWPE documents. *** As those documents are unclear, I am using Xastir to supply *** clue as to what really needs to be done. // How to Start your application and register with AGW Packet Engine // First of all create a stream socket and connect with // AGW Packet Engine ip address at port 8000. // If your application is running at the same machine with // AGWPE then as ip address use localhost (127.0.0.1). // // After connecting you need to register your application's // callsign if your application need to establish AX.25 connections. // If your application just do monitoring and sends Unproto frames, // no need for callsign registration. // You can register as many as 100 different callsigns. // If you register a callsign then AGW Packet Engine accepts AX.25 // connections for this call. // You then ask AGWPE to send you the radioport information. // How many radioports are with their description. // After that you can create your windows or anything else. // // Now you are ready. // If you wish to do monitoring then you must enable monitoring. // Transmit a special frame in Raw AX25 format // // The frame must be in RAW AX25 format as should be txed by // the modem (no need for bit stuffing etc) // You can use this function to tx for instance an unproto // frame with a special PID or any other frame different from normal AX25 Format. // // Port field is the port where we want the data to tx // DataKind field = MAKELONG('K',0); The ASCII value of letter K // CallFrom empty (NULL) // CallTo empty (NULL) // DataLen is the length of the data that follow // USER is Undefined // // the whole frame with the header is // // [ HEADER ] // [port][DataKind][CallFrom][CallTo ][DataLen][USER][Data ] // 4bytes 4bytes 10bytes 10bytes 4bytes 4bytes DataLen Bytes // // ASK RadioPorts Number and descriptions // // Port field must be 0 // DataKind field =MAKELONG('G',0); The ASCII value of letter G // CallFrom is empty (NULL) // CallTo is empty(NULL) // DataLen must be 0 // USER is undefined // No data field must exists // [ HEADER ] // [port][DataKind][CallFrom][CallTo ][DataLen][USER] // 4bytes 4bytes 10bytes 10bytes 4bytes 4bytes // // ASK To start receiving AX25 RAW Frames // // Sending again thos command will disable this service. // You can start and stop this service any times you needed // // Port field no needed set it to 0 // DataKind field =MAKELONG('k',0); The ASCII value of lower case letter k // CallFrom is empty no needed // CallTo is empty no needed // DataLen must be 0 // USER is undefined // No data field must be present // // the whole frame with the header is // // [ HEADER ] // [port][DataKind][CallFrom][CallTo ][DataLen][USER] // 4bytes 4bytes 10bytes 10bytes 4bytes 4bytes // Raw AX25 Frames // // You can receive RAW AX25 frames if you enable this service. // Those frames are all the packet valid frames received from // any radioport. The frame is exactly the same as the pure // AX25 frame with the follow additions. // // The first byte always contains the radioport number 0 for 1st radioport. // There is no FCS field at the end of the frame. // There is no bit stuffing. // The LOWORD Port field is the port which heard the frame // The LOWORD DataKind field ='K'; The ASCII value of letter K // CallFrom the callsign of the station who TX the frame(Source station) // CallTo The callsign of the destination station // DataLen is the length of the DATA field(the length of the frame // USER is undefined. // the whole frame with the header is // [port][DataKind][CallFrom][CallTo ][DataLen][USER][DATA ] // 4bytes 4bytes 10bytes 10bytes 4bytes 4bytes DataLen Bytes // 1.UNPROTO monitor frame // // The LOWORD Port field is the port which heard the frame // The LOWORD DataKind field ='U'; The ASCII value of letter U // CallFrom= is the call from the station we heard the Packet // CallTo =is the destination call (CQ,BEACON etc) // DataLen= is the length of the data that follow // the whole frame with the header is // [port][DataKind][CallFrom][CallTo ][DataLen][USER][Data ] // 4bytes 4bytes 10bytes 10bytes 4bytes 4bytes DataLen Bytes // 4.RadioPort information // // The LOWORD Port field is always 0 // The LOWORD DataKind field ='G'; The ASCII value of letter G // CallFrom empty(NULL) // CallTo empty(NULL) // DataLen is the length of the data that follow // USER is undefined // the whole frame with the header is // [port][DataKind][CallFrom][CallTo ][DataLen][USER][Data ] // 4bytes 4bytes 10bytes 10bytes 4bytes 4bytes DataLen Bytes // the data field format is as follow in plain text // howmany ports ;1st radioport description;2nd radioport;....;last radioport describtion // like // 2;TNC2 on serialport 1;OE5DXL on serialport2; // We have here 2 radioports. The separator is the ';' // 10. Reply to a 'G' command. This frame returns the radioport number // and description // // Port field is always 0 // LOWORD DataKind field ='G'. The ASCII value of letter G // CallFrom is empty (NULL) // CallTo is empty(NULL) // DataLen =The number of bytes of DATA field // USER is undefined // DATA field conatins the radioport description // [ HEADER ] // [port][DataKind][CallFrom][CallTo ][DataLen][USER] [DATA] // 4bytes 4bytes 10bytes 10bytes 4bytes 4bytes Datalen bytes. // // The DATA field is organised as follow and is in plain ASCII. // // Number of Radioports;First radioport description(Friendlyname);Second radioport description(Friendly name)........... // // Number of radioports=a Decimal Value. A value of 3 means 3 radioports // Radioport description= A string that describes the radioport. // The separator between fields is the letter ';'. // Just parse the whole DATA field and use as separator the ';' // // Xastir parses integer data like this: That is, it is LITTLE ENDIAN // // Fetch the length of the data portion of the packet // data_length = (unsigned char)(input_string[31]); // data_length = (data_length << 8) + (unsigned char)(input_string[30]); // data_length = (data_length << 8) + (unsigned char)(input_string[29]); // data_length = (data_length << 8) + (unsigned char)(input_string[28]); // ***/ // Socket communication packet header struct agwpeheader { uint32_t radioPort; // 0..3 uint32_t dataKind; // 4..7 uint8_t fromCall[10]; // 8..17 uint8_t toCall[10]; // 18..27 uint32_t dataLength; // 28..31 uint32_t userField; // 32..35 }; struct agwpesocket; // forward declarator // One agwpecom per connection to AGWPE struct agwpecom { int fd; struct timeval wait_until; const struct netresolver *netaddr; int socketscount; const struct agwpesocket **sockets; int wrlen; int wrcursor; int rdneed; // this much in rdbuf before decision int rdlen; int rdcursor; uint8_t wrbuf[4196]; uint8_t rdbuf[4196]; }; // One agwpesocket per interface struct agwpesocket { int portnum; const struct aprx_interface *iface; struct agwpecom *com; }; static struct agwpecom **pecom; static int pecomcount; static uint32_t get_le32(uint8_t *u) { return (u[3] << 24 | u[2] << 16 | u[1] << 8 | u[0]); } static void set_le32(uint8_t *u, uint32_t value) { u[0] = (uint8_t)value; value >>= 8; u[1] = (uint8_t)value; value >>= 8; u[2] = (uint8_t)value; value >>= 8; u[3] = (uint8_t)value; } static struct agwpecom *agwpe_find_or_add_com(const char *hostname, const char *hostport) { struct agwpecom *com; int i; for (i = 0; i < pecomcount; ++i) { com = pecom[i]; if (strcasecmp(hostname,com->netaddr->hostname) == 0 && strcasecmp(hostport,com->netaddr->port) == 0) { return com; // Found it! } } // Did not find it, create it.. com = calloc(1, sizeof(*com)); com->fd = -1; com->netaddr = netresolv_add(hostname, hostport); com->rdneed = sizeof(struct agwpeheader); tv_timeradd_millis(&com->wait_until, &tick, 30000); // redo in 30 seconds or so ++pecomcount; pecom = realloc(pecom, sizeof(void*)*pecomcount); pecom[pecomcount-1] = com; return com; } void *agwpe_addport(const char *hostname, const char *hostport, const char *agwpeport, const struct aprx_interface *interface) { int agwpeportnum = atoi(agwpeport); struct agwpesocket *S; struct agwpecom *com; if (agwpeportnum < 1 || agwpeportnum > 999) { if (debug) printf("ERROR: Bad AGWPE port number value, accepted range: 1 to 999\n"); return NULL; } S = calloc(1, sizeof(*S)); com = agwpe_find_or_add_com(hostname, hostport); com->socketscount++; com->sockets = realloc(com->sockets, sizeof(void*)*com->socketscount); com->sockets[com->socketscount-1] = S; S->iface = interface; S->com = com; S->portnum = agwpeportnum-1; return S; } // close the AGWPE communication socket, retry its call at some point latter static void agwpe_reset(struct agwpecom *com, const char *why) { com->wrlen = com->wrcursor = 0; tv_timeradd_millis(&com->wait_until, &tick, 30000); // redo in 30 seconds or so if (debug>1) printf("Resetting AGWPE socket; %s\n", why); if (com->fd < 0) { // Should not happen.. return; } close(com->fd); com->fd = -1; } /* * agwpe_flush() -- write out buffered data - at least partially */ static void agwpe_flush(struct agwpecom *com) { int i, len; if (com->fd < 0) return; // nothing to do! if ((com->wrlen == 0) || (com->wrlen > 0 && com->wrcursor >= com->wrlen)) { com->wrlen = com->wrcursor = 0; /* already all written */ return; } /* Now there is some data in between wrcursor and wrlen */ #ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0 /* This exists only on Linux */ #endif len = com->wrlen - com->wrcursor; if (len > 0) { i = send(com->fd, com->wrbuf + com->wrcursor, len, MSG_NOSIGNAL); /* No SIGPIPE if the receiver is out, or pipe is full because it is doing slow reconnection. */ } else i = 0; if (i < 0 && (errno == EPIPE || errno == ECONNRESET || errno == ECONNREFUSED || errno == ENOTCONN)) { /* Sending failed, reset it.. */ agwpe_reset(com,"write to remote closed socket"); return; } if (i > 0) { /* wrote something */ com->wrcursor += i; len = com->wrlen - com->wrcursor; if (len == 0) { com->wrcursor = com->wrlen = 0; /* wrote all ! */ } else { /* compact the buffer a bit */ memcpy(com->wrbuf, com->wrbuf + com->wrcursor, len); com->wrcursor = 0; com->wrlen = len; } } } void agwpe_sendto(const void *_ap, const uint8_t *axaddr, const int axaddrlen, const char *axdata, const int axdatalen) { struct agwpesocket *agwpe = (struct agwpesocket*)_ap; struct agwpecom *com = agwpe->com; int space = sizeof(com->wrbuf) - com->wrlen; struct agwpeheader hdr; if (debug) { // printf("agwpe_sendto(->%s, axlen=%d)", S->ttycallsign[tncid], ax25rawlen); } if (com->fd < 0) { if (debug) printf("NOTE: Write to non-open AGWPE socket discarded."); return; } agwpe_flush(com); // write out buffered data, if any if (space < (sizeof(struct agwpeheader) + axaddrlen + axdatalen)) { // Uh, no space at all! if (debug) printf("ERROR: No buffer space to send data to AGWPE socket"); return; } memset(&hdr, 0, sizeof(hdr)); set_le32((uint8_t*)(&hdr.radioPort), agwpe->portnum); set_le32((uint8_t*)(&hdr.dataKind), 'K'); set_le32((uint8_t*)(&hdr.dataLength), axaddrlen + axdatalen); memcpy(com->wrbuf + com->wrlen, &hdr, sizeof(hdr)); com->wrlen += sizeof(hdr); memcpy(com->wrbuf + com->wrlen, axaddr, axaddrlen); com->wrlen += axaddrlen; memcpy(com->wrbuf + com->wrlen, axdata, axdatalen); com->wrlen += axdatalen; agwpe_flush(com); // write out buffered data // Account transmission erlang_add(agwpe->iface->callsign, ERLANG_TX, axaddrlen+axdatalen + 10, 1); // agwpe_sendto() } static int agwpe_controlwrite(struct agwpecom *com, const uint32_t oper) { int space = sizeof(com->wrbuf) - com->wrlen; struct agwpeheader hdr; if (debug) { printf("agwpe_controlwrite(oper=%x (%c))\n", oper, oper); } if (com->fd < 0) { if (debug) printf("NOTE: Write to non-open AGWPE socket discarded.\n"); return -1; } agwpe_flush(com); // write out buffered data, if any if (space < sizeof(hdr)) { // No room :-( return -1; } memset(&hdr, 0, sizeof(hdr)); set_le32((uint8_t*)(&hdr.dataKind), oper); if (debug) hexdumpfp(stdout, (const uint8_t *)&hdr, sizeof(hdr), 0); memcpy(com->wrbuf + com->wrlen, &hdr, sizeof(hdr)); com->wrlen += sizeof(hdr); agwpe_flush(com); // write out buffered data return 0; } static void agwpe_parse_raw_ax25(struct agwpecom *com, struct agwpeheader *hdr, const uint8_t *rxbuf) { #warning "WRITEME: AGWPE Raw AX.25 reception" } static void agwpe_parsereceived(struct agwpecom *com, struct agwpeheader *hdr, const uint8_t *rxbuf) { uint8_t frameType = hdr->dataKind; if (debug) { int i; int rcvlen = hdr->dataLength; printf("AGWPE hdr radioPort=%d dataKind=0x%x fromcall='%s' tocall='%s'" " datalen=%d userfield=%x\n", hdr->radioPort, hdr->dataKind, hdr->fromCall, hdr->toCall, rcvlen, hdr->userField); if (rcvlen > 512) rcvlen=512; printf("AGWPE Data: "); for (i = 0; i < rcvlen; ++i) printf(" %02x", rxbuf[i]); printf("\n"); printf("AGWPE Text: "); for (i = 0; i < rcvlen; ++i) { uint8_t c = rxbuf[i]; if (32 <= c && c <= 126) printf(" %c", c); else printf(" %02x", c); } printf("\n"); printf("AGWPE AX25: "); for (i = 0; i < rcvlen; ++i) { uint8_t c = rxbuf[i] >> 1; if (32 <= c && c <= 126) printf(" %c", c); else printf(" %02x", c); } printf("\n"); } switch (frameType) { case 'K': // Raw AX.25 frame received agwpe_parse_raw_ax25(com, hdr, rxbuf); break; default: // Everything else: discard break; } } static void agwpe_read(struct agwpecom *com) { int rcvspace = sizeof(com->rdbuf) - com->rdlen; int rcvlen; struct agwpeheader hdr; if (com->fd < 0) { // Should not happen.. return; } if (com->rdlen > com->rdcursor) { memcpy(com->rdbuf, com->rdbuf + com->rdcursor, com->rdlen - com->rdcursor); com->rdlen -= com->rdcursor; } com->rdcursor = 0; rcvlen = read(com->fd, com->rdbuf + com->rdlen, rcvspace); if (rcvlen > 0) com->rdlen += rcvlen; if (com->rdlen < com->rdneed) { // insufficient amount received, continue with it latter return; } while (com->rdlen >= com->rdneed) { hdr.radioPort = get_le32(com->rdbuf + 0); hdr.dataKind = get_le32(com->rdbuf + 4); memcpy(hdr.fromCall, com->rdbuf + 8, 10); memcpy(hdr.toCall, com->rdbuf + 18, 10); hdr.dataLength = get_le32(com->rdbuf + 28); hdr.userField = get_le32(com->rdbuf + 32); if (com->rdneed < (sizeof(hdr) + hdr.dataLength)) { // recalculate needed data size com->rdneed = sizeof(hdr) + hdr.dataLength; } if (com->rdneed > sizeof(com->rdbuf)) { // line noise or something... agwpe_reset(com,"received junk data"); return; } if (com->rdlen < com->rdneed) { // insufficient amount received.. break; } // Process received frame agwpe_parsereceived(com, &hdr, com->rdbuf + sizeof(hdr)); com->rdcursor += sizeof(hdr) + hdr.dataLength; if (com->rdlen > com->rdcursor) { memcpy(com->rdbuf, com->rdbuf + com->rdcursor, com->rdlen - com->rdcursor); com->rdlen -= com->rdcursor; } com->rdcursor = 0; com->rdneed = sizeof(hdr); } } static void agwpe_connect(struct agwpecom *com) { int i; // Initial protocol reading parameters com->rdcursor = 0; com->rdneed = sizeof(struct agwpeheader); // Create socket if (debug>1) { printf("AGWPE socket(%d %d %d)\n", com->netaddr->ai.ai_family, com->netaddr->ai.ai_socktype, com->netaddr->ai.ai_protocol); } com->fd = socket(com->netaddr->ai.ai_family, com->netaddr->ai.ai_socktype, com->netaddr->ai.ai_protocol); if (com->fd < 0) { if (debug) printf("ERROR at AGWPE socket creation: errno=%d %s\n",errno,strerror(errno)); agwpe_reset(com,"error at socket() creation"); return; } // Put it on non-blocking mode fd_nonblockingmode(com->fd); // Connect i = connect(com->fd, com->netaddr->ai.ai_addr, com->netaddr->ai.ai_addrlen); // Should result "EINPROGRESS" if (i < 0 && (errno != EINPROGRESS && errno != EINTR)) { // Unexpected fault! if (debug) printf("ERROR on non-blocking connect(): errno=%d (%s)\n", errno, strerror(errno)); agwpe_reset(com,"connect failure"); return; } // Aprx will snoop everything that happens on radio ports, // and receive frames in raw AX.25. // Queue necessary configuration parameters on newly constructed socket agwpe_controlwrite(com, 'k'); // Ask for raw AX.25 frames agwpe_controlwrite(com, 'm'); // Ask for full monitoring of all interfaces } /* * agwpe_init() */ void agwpe_init(void) { /* nothing.. */ } /* * agwpe_start() */ void agwpe_start(void) { /* nothing.. */ } /* * agwpe_prepoll() -- prepare system for next round of polling */ int agwpe_prepoll(struct aprxpolls *app) { int idx = 0; /* returns number of *fds filled.. */ int i; struct agwpecom *S; struct pollfd *pfd; for (i = 0; i < pecomcount; ++i) { S = pecom[i]; if (S->fd < 0) { /* Not an open TTY, but perhaps waiting ? */ if ((S->wait_until.tv_sec != 0) && tv_timercmp(&S->wait_until, &tick) > 0) { if (tv_timerdelta_millis(&S->wait_until, &tick) > 60000) { // Verify that wait is not too long -- system time jumped backwards? (but not 68 years..) S->wait_until = tick; } /* .. waiting for future! */ if (tv_timercmp(&app->next_timeout, &S->wait_until) > 0) app->next_timeout = S->wait_until; /* .. but only until our timeout, if it is sooner than global one. */ continue; /* Waiting on this one.. */ } /* Waiting or not, FD is not open, and deadline is past. Lets try to open! */ agwpe_connect(S); } /* .. No open FD */ /* Still no open FD ? */ if (S->fd < 0) continue; // FD is open, lets mark it for poll read.. pfd = aprxpolls_new(app); pfd->fd = S->fd; pfd->events = POLLIN | POLLPRI; pfd->revents = 0; // .. and if needed, poll write. if (S->wrlen > S->wrcursor) pfd->events |= POLLOUT; ++idx; } return idx; } /* * agwpe_postpoll() -- Done polling, what happened ? */ int agwpe_postpoll(struct aprxpolls *app) { int idx, i; struct agwpecom *S; struct pollfd *P; for (idx = 0, P = app->polls; idx < app->pollcount; ++idx, ++P) { if (!(P->revents & (POLLIN | POLLPRI | POLLERR | POLLHUP))) continue; /* No read event we are interested in... */ for (i = 0; i < pecomcount; ++i) { S = pecom[i]; if (S->fd != P->fd) continue; /* Not this one ? */ /* It is this one! */ if (P->revents & POLLOUT) agwpe_flush(S); agwpe_read(S); } } return 0; } #endif aprx-2.08.svn593/configure-stamp0000644000175000017500000000000012313357152015473 0ustar colincolinaprx-2.08.svn593/doc/0000755000175000017500000000000012314022011013204 5ustar colincolinaprx-2.08.svn593/doc/aprx-manual.pdf0000644000175000017500000153011412314021035016136 0ustar colincolin%PDF-1.4 %äüöß 2 0 obj <> stream xuj0 E J#6$,tQkBg߯dNs}ۀ^;2ayٙ/}zrn46:[C"$^a ŁWs~[ym(q*7(AXHEk,YhKA[rj>ڌ౿0Pz`},C֟T>9vعP: i4rz4Ng= Fh$CnUDbW1UQ4ԴMI\_i'f3}4X6sM ~Hd"iw%ӧޏ͞{ɗPE@c:0͕O.mi/K = endstream endobj 3 0 obj 378 endobj 5 0 obj <> stream xMk0 >IJ0 nevig3 "-ғWСn ';ڎL <ޙͼ4 ,/~,oO(lN_<"nSk#L)DƭYp#݃lAHknr+{d#+%/D{=ZvˈR9S%CLfcoUb @df-XX#cue HnHԆHA,PVHt6Avo8;W>إul6Eɪ8k:AX#UW'CѦJޏhvUR{(&|ތzSk&N;&UW endstream endobj 6 0 obj 376 endobj 8 0 obj <> stream xM3+|4MK}mrr&Rv/M]`z>z%ޱꩢ Lto;#/9=iAzONOҽ ؽk Q{?v|‰ DSՁ<>y997^]ʓ|ߞOdD%h=CmaH|U.偊I_\z"CdJwހ*SWw wqMln$wv+Oj=ƛbpNm8ԋ3.4SΌ̨ p^z*XG(V?ӲD8`+fwլ2t xdxgNۙ;kL حKQFa3R-Q| k˺r,YUAN)/z4?V>'uP)%lw `j)nÑD5Iͬ-`roE;fčUA\Vqs\G{]t/EБ#Sћ&{wDdXc?bcpEZ-7EHJpMIjS!f 2.9tB`gjϰv{ Lv1tV%@Ý&3Xa<֦-pw#I7C',WK>uu$_[NNFНPgsntȹYl.;;5wDc5r<;9t<]*?kbDs }|}aTײRKJ7º0_Z.b--o4$о-p@䘃^9]xiso~dZ[?sݫ+OH!r~:4S5ԄȌχil})9gӬ̬n*R*U{ҽyoQ U* YVs<LP~ Ҍ~зWf\ֶ7'wYl% ddĖ?Yʲ:#U5RF.;=-oqo~h1&Z4A~7A~uHSU%Qˈԯt؂ƀ'6kRMGnIf٣ϊ DC@]h{pE_1 ..jilp&-q+ᐵu=u1YLOEǛKxN c\DԵG^9&)ͶYgG5=nN7 L/A$Ħ$1o۰ pGϣk0|"-z7chl\W99GiEU~ 41K6ZH =bi"WE sY11 tkLu-3ip1|<4So.a 6qqT7}z]%*6Tm>s"%85Ԣ*OS-X{V+>&ֳ+1G6gnx 3iE(*X{37hޜ̢d`f{8$|3 Ъ )RU21'%,v3z(>u^ g+Kg(#W{s}~%T5' 7*KK(!*r3W-{NDXݒ*{uC>XؾT),_]r.$|T3#ZQ|\4ci}Μ5rT%4S1E~8;R>MSGF& ;ݦ2"` -.Bjx0jZ IjsOQ' |^UvVk?vVҽ~N/|j LUxs l1t0b깅-s iϧ})9Ljwua}q\zlPL&27տ77Q ~L9E2{[-DNڭX8oYh:kCon̘s -tPn>K&>!-|ZE:؂Tu&ׄClC#]pF&5uƩZcP3lMǿ'ӝt!g$ǸfE>MP3b`Um #Xcc IΛ_$ F[`JTƆun>ӻkd%ͭʦm`-  uL+Q1Py#ꧩJe7{4&XM{(HzЀv1䚓TuTPO)y\`qN#ΫY7_S|KdwT} <╼M&cL==c+B{ފJ [-h1f0/[%fZ3gl=0Mv)<]\ m-@n;&uxk>>Ž`G&18::ʮ݄7pv6`WV8J1SfS9tW9zS{qn^I7WmH]!BJ85 endstream endobj 9 0 obj 2966 endobj 11 0 obj <> stream xI+7+|` =!r]#VK%~y@EWdCW"+Injt3?_o/ҍX Xq+Vo%ݚn '9ԖLp,wk%qKpro;~}%{5'r#W3τH\QfvrKE&.D]w%:L՞0M͙=b, (nj}N[~Qb,S?ن}M-wL1}FE0)+MieJJ:nS9,/PΙRn5i:58=C]WQ^1 <3?k` QhzcyumfQjux@3ab@Ձ, eI࿄ۑ̞TSiF/ܽ,C Z6UkTanS =ӺHGSmdѝI9>w>8ǓQ3|e+35J;MkJB5͔=9#rL)x"f! uQM <ZbGGX HctDhXzS.^fRPםe,̍n=ǎۙ*˺^%6s{)>ZcHľⅯOTX;7tJ?9(q4Zw>EikIVF3T!w)9*!e{t^J"$ fl;'J+TwoȀw=?A˜ӕSK,DBPZw{L $דz|RU 9sxG}.^Q&s2a-S/^ XW j>8Xxxt )B݆6ܬ\ytdZ~o;(Vۀ:$ $p5fY Tӣ"z&2k yw fN%@^M io'` `XV>~ͩiW ;vhnsϰ&ќ-geu$ϱz`#MD=~uе$9zf!R z#z&•jEaIw,✤IʋJB8 %'81Qhzw&12/LJlC"[$2|(goQVL{f -joCM=AN}kFI O6JI\kDZfDEf4t Kqic^ͪ9Z޽(b vޫ5^26-X 䯭/a!b2* \ tjy/Z8!>|/|_JXŗ*.ďI2݀-Y1^6&O05UfV޴}ZG<Ԭb`iHLdQmeS(McHA@6IBDwt0kc4j0+UNy5#Śg&P2XjMp[|[{wRDuĪ$ڈc]#^ it.S-~i`r^Ȥ%,^~p˹ &;Lt98I]5ߚVx_\Q1=$cr7;PljRɬ gbc )kZΔ-ˈCϡ5JW͹ÊNT]zەbkBVr;/e2(:n@̑J=\+RpV8E{):F[)0Z'+)r M < rnm}eLy]:> C\H{z* o;>H=L[rkgpZ2|j+j~5:C  <3 95LT՛X`4Zǡ@\:MkZ4Yw)2IfyoʧҬHu% )1hܤ/s}>É}5O;VtЂXrU*S _:r*B9l;4(I7|:'ltZU#1{K.>b QK,rTKѤ%4]W*Ĺ?M X!zV \s %ؔ 8TzB@qk+OTq2F 'G.NN qu-W?  endstream endobj 12 0 obj 2286 endobj 14 0 obj <> stream xYˊ+7+zw4xOϯwyv/w=i3}Yie֣>+9?uYYEף?˺>;~qoçni6eTTY6?k'Vy"AE|k {|HOa&E䖝y +#6I6[y%0Pm?wmD C6gנDd#DͥСSy Vr5F@s8,II1EӼKR&q)d.8$odAYKken[֏?6&UT7jW,3ˮ#BH,MzP(1X.n,;ycEsSp4K1ā|l72OR ѰvyT :ѣ,4(C@;^ ;5Z'aϟ|AMG=}tԀ_$0sR\=GFEqC*I ~J30}a8Rյw%uCQc0^ ZiJqԩ/HSS@ڏ4I3G'}ǩ$pQH'ky# ]ů֒j2UU bd5vK|2ljnߙOL'i{O)kn=֦V-^Qj;m\qFsyrC}zF^@Eq&sY!x=Z_ygUU"j^nܯέ|v[pdQѥ#յ ~>J(&փE6M㌥վЪf9b/Dzny>oT]d6[]сjԨUgPR-4ܷlٸF -ap9HY u߬<Ίep#0y<|N&fzt vI2kO%΃\]Cs -F.B$ 5y"ю晊sa:3SB!l.l6ҥCpe mV;>R~> stream xVK: ϯ0%?CL(]qBW8Wv?c14-NmN'&~x6A$dNV_a> ‰[%gnٻ_Ru6X4p{@Ag)2}^t%FC{=k+v^ D̟/L/O&%Y7'-&~P.Qzh~B"y78};$yyT endstream endobj 18 0 obj 922 endobj 19 0 obj <> stream x puښuj' IHH= !AfQTT D` SEA;|o|oG+O`mΞ=ɓ^z7p-UL8:[2lI]dKː~zO2xsMΚ5ĉ~0vDJ?lIWn˖DJlI.͖gϞum\-PȖ0 f![,dKl -`%BYȖ0 f![,dKl -`%BYȖ0 f![,dKl -`%BYȖ0 f![,dKl -`%mr<<^-/ʝudK` %Ef![RdK`%Ef![RdK`%Ef![RdK`%Ef![RdK`%Ef![RdK`%Ef![RdK`%Ef![RdK`%Ef![RdK`%Ef![RdK`͖  5Q>AVBCC[0`64_i*{o^em*9;)5KBRi]MܰRx_fu%9zx44;Ǯ⩜\3cי/gK{@r˯IcQ^s [Oqg42̖UWmIm)#2*7veK>7-_\RnMLoo)kowdK)ɖ:!ll:`/.Y]ZH_yMZ\pݽO<₰p/ S& z6KzV_`kdK)͖YTTdw@pHHɉ/Ֆv٧אSf?Hw=G/N}Z`TttΤ_hkdK)Kٳ{w]g定sz /C%:hV ypG>f>谑c üUw0WR2íJn4[xoGoԭOiΝ o*9Ԫm{1[+s [Oq)Ugˣ7lNLLnq}sΪQ؁^\M~ke=%eKHr8[nXjVFͱ7[Jҫ.ɶmMĉ|aHQ15Kq`dK)Ζ#lS#%b|[ },>T+e3in0xkLGj}_H@laRXSW5׾/9ym{| YuWCKq`dK).͖p,[_fdN> Vl);}ܚ2>*:_M/];%Ȗ]$eDχtQ]6য়Tq5;6i1Ϡg,?`uhJKkdK)-)%dKl BȖ,dKl BȖ,dKl BȖ,dKl BȖ,dKl BȖ,dKl BȖ,dKl BȖ,dKl BȖ,dKl BȖ,dKl ʽY7l Lmr ~?l EYȖ0 f![,dKl -`%BYȖ0 f![,dKl -`%BYȖ0 f![,dKl -`%BYȖ0eyO?.7O?ưG n^-/ʝuρ2|@Ȗْ"["[RdK l >lI-)/,%(%EȖȖْ"["[RdK l >lI-)/,%(%EȖ~ƵGxr-)%l rk;OխuS%E<-=~Ȗ)eR!"[R^XdK*GlEAѶc!s):P'"[ z'\A6izP,?z}했(M>lojdE;N+}t6Fr4R^xUlh9? xE"[zȖ[dK '~*8"WϘ[Xu7kwvҭ[ʆ[gAr5,<\Cs>hՕoLD&7}X|Xi|͜Wtlu 3/ ᖻ{d(Sgذ)4)?'eKQ5dFɁg|/?#E "[j8-}mP|6w̒ThZGꭻ4n*9$3rGOh^C -ײRi?{PdFɁg|/?#E "[j8-HԌ.6'&O_Llfs7=!1I.K6U-'­yҽVm$+Gito|)JKlh99xE"[zȖ[dK '5e3*Wkʷ mjLm!s:_RegiPOl|)JRCElx^@ <"[S-=Rdˀ-rHh0cQl^5SVlhS)Ng[m劋}G<TQd=G/V^Ss{e'F7ƗåGR{5lOEH-Ȗd5)x9Sn-Lr&rrʹTH{,kryFw峣͟UY6ZG\kb|)dKK˖jW gKG+iWVG+poOQs$w;Fbo[FжcZu݆VRX؋KV=E;kwvڽ[67"F< 0群lϣ^:.YP쯮LMkxHm\%zzLe32*ZZ*4dFɁg|/>]R(Nɛ)7oz:V-5e}YJ-5dKdS;jTQ5c譟>fB\ˈMUwX%wl۪MMWK>GQ%wc4ێ}XoP .^,e36= wW gKLVTkQjGA_O*petsGSoRlн1f߭Aͱz٣wtLlU5&n y=nb%}o,䕑'gE$6O֧wjot[z}ȨhYnj{4>)3+,<\= [Ll]K1Alx^@ <"[xz|4[*{?c\]ۚn뿐[et?},5Qyc+Eמ}xMjɻvS3;aS!B+.ޖIL$3jʼ%,1:&F-;w'FjdO*3L:}aFб"[jxgK TdKVcϏ~-jh PlYrˠ*W7V&1`wyOQy &[W{hʧe͵jJ6k퓇WQP|}했(M}r=XJ6/f,?SY,*XlR.,5tȖ.}n>r 5>Ȗn(Ǟ<"[3|%h|qJgG%dw꿌ڳHOlyOAK5aʓWsg:Jl .o漢g%^xS+? R&oZ/(=sMfkuzV {*ԇ$.J,W,%$$TFc^}C%<_CNJluuXq<2P<1iTdK7cϏ~->v|+['Ͽ@O;՛w`PoruvJT|o+ΖjѿUL;aAnB"/̗i _6gtoPtLlS}>)?q{a46˲QFwk@-5\#&SY{f{)OȖjH;^-uL[f/5zMJN|;b#[n)=l٨B2$$TgZ彼A,ߙ=z[Oރu޸o-5K[]k@-5\Sznʼ%%47m; 2|} {){ʱOȖj5:?b6r̆=6*XUƇz^4T-5G^Mim㇑-[[O[~fK­yҽVmÕci)ƴeٵR;_ȖR-#/@@v-%:&FjĈQʱ>*Jm|AVgW~ ٽk@-5ǿlIya-ϖ>v|.[?&mHH*3iHP[ &-b]-#eN~A;^:. Α7TXRd\J,:${.ZsA=6ė)z'̜_T}5LNi)-gUصR;_Ȗ D#eL)yL|KlR*w5!ڝ ^W=~):&FVMNwzզB'"]zѼ2}}/9z%Աs墍E.1ɖ OgS_seYETKٲwʧR~З*Ǎcʭ^rM{m[~Lo]벖3s>:ܵ*>rAYuEe'r>[zjiz54f|(xmr쵽Eo۷}'lzZfݻw Nbn?OCdi殕O,r>[:z돋lr=))7~ }lruȴ̖CY[nJFĤݵƢ3QҾe]s}Pw64*ʖi3[)݂YuZs_\:M̖_DȖ)e!'ٲU7dKBC4=lf+>rA_|jSaqԄJW0dN*יPih@Z3{D*5THH\Y+el"]TdKR De48$${Ds[IlF?;j]qW-XFM)iHM}Ϟ={サw^~}ܸq,M65k։' mU7>9+-ԍtl6[Eޔj.;\=Ml2=[j"'mҥR-*W 0r3,)ykXs{̈́ Mr譟,( \Agm_~6D`li)+VO{PiI?[ZFJٲAqnk,;{MԆl!H#eVl(RlH- DJKhQ+Wu_ٴ-|-]ٵG9ƾe=EP޵6@o46%H3tVR9 DnȖ6#Cҧ%̖6#l9n,|*>vliP1b=G/V^#﹔]4z0"[6z&EEH9-xm([9R,]R!* SVO6|U7G[s;&(_|eٸ䐑le]Qκٸml)ׯ_o+ǎ-~T-?,>ܢBBRslhFpk\Ϫ,V8ĺ!HȻ[fdd4)U+VP|-.{#lYu[y.ê;Q.j^R]]CRcbHO-ҁC#ī6:>v<-_x/j ƫN9??NliIjch$[>\ZfK׿:# [?)~VYuZU M)}JOEEGK9W};)e{*od*)[OUFMpw KJN|c 3U9K,INi 1^:. ٲ7TXMs@$Sliש/-EM̖,|@m0xgǙl`/|G]CR:ef!٣U?pރ7YʷsYeuHE|'>|tl;y]Qm7GlC]׹yԬɥenΪ?.U`~孵޺KM&\m_󔇃,hNұsG^6*IUK>F>w/|-n/ڝC"AS#ֿht -˺]e0>T .e{if%qq']:ϖpu شl>-G[B+dKD ϣɥEpg͜׶cZtLv͛朦'GjdQ2nlP6z3i u'gͥ]|سIYz/㒓WFC9yr>9tYl~!2|[`V',vyIOgLlv KYz}LYʧnjWq+:|Sfjն{_fˀM;&~'~>7z<*/q ]{}JΑT'W.+dL$=m#jʼ%2%?[v$KzG'Tf(/[#ht?e3ׂOavuuruokͯ[z<\qYS9:1I2&} Lw3[Z~!+[R/oΖ-i21[\>Mܚm?6FG ϥjK}zTNu!ΪӖk`eKt|lYrˠ)W7V%Ͼ<Ȼkϫ}țbi߲-NnsV:h$@-5B_-}|([v-[?~% SwTev>י nIIj2'57QZ$oS!ݤ>,> 4>VYDHHCWPyԝ lҕAU[_y7'-e\t}N2f]->rA^CMe`;`-5B_-}|([v-sW^)z-Of=+L7(:&)OʏHjK seܴYڼ32*Z=̝3X~-]'qCFƕcĈQ%'8il_kqo/fKNCl~rPgi^i)?|~ּҪMTttXxxpHr(:zo少c6쩰9Uj7>Աq3\7B9;Jcl_kqo/fKNClhgnOpǔәgABlR)_\A_=ҲEٹ2A* .0spj s-˚%%/}s&R=? }!ɖU-͓<0xI4~{\^P}^f㞣E͵ZLj"W5gД3X-vVVởۜjܴYrة/:,#I3 VRX[N>dK /7|!ɖU-$xI4~{㏶7VeXX=Sr0,?ȖRʱܢBBRs~wr,[:?K;'L[^Μ9$OZ^}w]'[ZH liy''7hGHءOܩ⋯_\duFle]=ٲAg.Ϭ<,Ҿjc˒˭ڶ7r:INbu[-ߔM:`-5ytdK7/l[2ӎGedT\_Pyr$'7hw̒[T+<>䍕˭v-03}8?}墢?.u$[jxp/a`d)g:RdKϖ9o_fˀM;ɖNe'y>&IzV ?RV) 1%Hc%j^~Wgk>U":FV`SJys4g}dk)/rkC,#SWfk1M!.ӳyw՜s$[jxp_|2YQA2|tοOҳO2[lq8[RΔoeK"[jsy`VEI)Mine}dd8-\ <#ْȖ)eR=C⋯f翮>88/LZ:3,LMj]5熍.STttjFc'l*9u?gt9'$&IOiW~o/6'९h-VͱOȖTdK2`l!s_hxD3cWκkn|}했(ͭ}Y{ҘҪr.u>d䪬mq>&dK7]󿁧_dK*GlEp]'G('>k0q"e?MleC}ߔ͜Wtlu]FX,L Ue,1Eʜ3,O'&Z( ?٨C:PdKH <"[S-=Rdˀ-Ck6v]n3ś AfJ0sZ3>m+is&Nohe=emSZ0Y=Vdֹ"[xFZ`ҟl"[l-5LSV*-<2XZ<=zkhA ,kG>DdKW3H <"[S-=Rdˀ-CT,Nx熮ch-[kUk^\%'lrfi񃁧_dK*GlE0}GNQnћ?t6%[FDDl5]PY#OHjnCDtE93/?#E "[j>D{孵;g*ǖm-zN'aJTPqvfs%nV)B#s"+ʙg~-Ȗ)eRuCt}Slʼ%!UʔlkҲq_eu<8ew`Yw![xFZ`ҟl"[l-5\=DwV5ajTtl:#"FM):|VliyK5.yV:LHEg͛|)9iDŽl醲ki񃁧_dK*GlEpyy.~q䔖ASȖQœwCu\B3ieuo6쩘(?,<\s&Пr DY7YC#o:_dKg~-Ȗ)eRG |z-d}YPWm*ldK>4ٜ;%Hts5:/?#E "[jxv:- /2*:&IzV䕑'Iܐ<[ lmEx9-`ҟl"[l-5/%E"[zȖ[dK lIya-lyG.oeKYʲl+Ȗ7 ~YΟ?< 8< OE<-DJPc"[R^XɖEȖSw-)/,%l_dK l 0[ْ"[LEȖSw-)/,%l_dK l 0ży3D?[ct7ܟ-Ȗ0 f![,dKl -`%BYȖ0 f![,dKl -`%BYȖ Qɖ CD-H9v(wֹ;?-_"[-B$[lI0 ْl `%,2#"vL8dx9U@rxB_,%WveKKM.=]gr7]&dKௌdKj<'G  RZ|>:l em8޶c=MidK8->. kwl~ IHj0pmǤ1E ^Z&&kw@lӸf}`fj 9]jԌ.#NTrzm+jʼ%Y=%FĴO뜝;iыjKk_&Kx[&7L$=ɖ 8-r͖,j9hՕoLD&7}X|@IM%o漢g^xY({D*=dž=HdMqawRQ&o"eN~Ak l RthնM#˳p=i3[^,1gti*'W.O9_.OPg|iGA1~uܤODwRie u1%cNfC_~|l,aOf}d0[޺K3C1=S.yd\~Oqi[?޸r-[/Eɖ 09-HԌ.6'&O_Llfs7=!1I.JU-'­yҽVm$ +Qj4[_R~&&'5%՚?hlyfZe7\I}J^o>wRu%%4Nf!٣ÌE67K4SV2-?;̵̳_~$ٲM;\q~\^PgXyN͵o͖ƗE͙lf>;;gխe5 Wd0[UvÞ i%{ |T.oWhS>Y~ĵ&ƗBl)/Vr9ĈQru씙>O]h;*o0U.OT.?Tϒ7ޕELHmGJWVO9Q_iݮ-@7[J<{{KavLP5? {qɊǾ|g]wH :⹼O(Ƿ<%% *u\B|n_]yI3+̙4C*pk=&U{?W_N--s dmcW_ 8#&x&L\iOj&Vx-%ڸۦCjtLll:eLRn[j1}ZzT<6˭ z,+.ޖR;޸3qzBbSO<4et0fnj 9F }R3;aS!e?á? :O?rͶ}aaa'oծCjkz5{r9U^zUrӢu2^vKDdODDklvQtj*2=[g`7s^ 5׾U [l-&LU"MZs':U}ݵ^,24T&7[\o"eN~AkH{a)I2יK"JO׽·Aًil SֹYɜŵf,^& YI|([)g-cbHrRj&qSylʹÞ}Ns&Ͱ7[Ws&N#yUMe村QPN9AԜP3 }W?[)0=[JfHĈQ%'4'w?~וwK{מ}Ζ=h'54n-Y> >д^6[)1=[n)=$.^RYpHHf^g-|GI}n%JN|vҞlY~e$FMn԰-[{a\t)x!W'ռZffIK\vƚkk&Tvɉt8[eew?謭φ0[x'߲̂䴅+=~I/wn4[V^ftKj"gd6Oۮ{Ȗ @0%IS*\TmҮeTHHWJɖzJ㺢2u5Qq_%Ig%6O.XղMoM*Ws͖ F3H{9-qC疍F1-q4>kG#""3RN4NKxUꡊȖ2=[?)PVYuG.<7Y{)=]--\蠟\vW2(Ɂ#ҳԨ SnVcH?[24Ǟf/>#s~}䔖2~\K_%49[WW^fÞjjDaz8gsk ժ^^yy"""_ykf޿fͶ}SyfǂԱsfՕo>6*IUK>Ȗ\{N]͛9mǴ蘘v͛朦'GjdQ҇dVq %et)/i\~Oi*O~ eKʇ=I:ui#+#OJ!<[ :l =PdKȖdK-ɖf![-B$[lI0 ْl `%,dK%YȖdKx6[ݹ?-kDdT|c Qd>ZdK<-k r7}=j-`,>rAɍʪ6M-%[x'wf#7ۚٽr)#wkOK77[Zɺ-lYW_oѲ`i3׬c&[\\37v՜6rLNQѩ]FЯmD:޸3qzBbvYiu_pdK:[=cb%GD<3CLϖ5""69\wOSZyW-*#ATMz^ [8urG"i|ًJO5m^5اMpppyEVݕp8c0.efI-RVy:6qBσOVCBdeɖv1=[޸b]+!0u+<6;-'̜/- 5N_/6gV^S6UgzVk6="[azl$^f}dLɖ},-LX\`?5~]u5W#%gK%Mxg'eJ6A kѲf7 6Wl `R=zzVu[2""R'[k=z'+I_韐Ԝl `pݽWZ۱sZiixukCHYυpJOr_-p~k0xrȸfS-);{ٲGeJg/)˚E +WrgQFEGKf7Y糥S/ǟLw[0q"Uy6oźIK%Wn://.]2dȨ1uN~A;ʱ+^:.4ٲ7TX.9f_9/ɖ qko<8OFd}X4T$.>J},?9YY+bIȝҺȖNe$=^rEnHlܭOh-YdK<-Ȗ_-ɖf![-B$[lI0 ْl `%,dK%Yϟ?g:toࡇӟ[^^N3$[L82[2 ْ gKB&i޼jo۵kaO?p:ͼ-Hꫯչn5ɖDJlIonȖDJl٭[7"%@pi@@![,dKl -`%BYȖ0 f![,dKl -`%BYȖ0 f![,dKl -`%BY-[΃DCGGyasv=G/f.Yw/mD:޸3qzBbSaaxol8E "[j8-r͖,j9-ub6Godsš?&MWoGd-W͔VmvgiN0uQK}l>h~-}e:H3> l7E "[l-5̖o|txd6r\ ?cnaթ߬YکKn}+QAgAr5,<\Cs>hՕoLD&7}X|X}Zh -y3>[]wŒDx&&TY=6쩨|GJ k"ӿr2a|Ĥ)s J\k'-}bkb|E"[N-ȖNfKKVmld:jѲ償_5o_Ҫp9fKKQZ-}ekbH38 l7E "[l-5̖+lCslm\f+ iU9刈H-G۴Om]6E3l^c0/2plEp2[-f,ʷ?Y\mfªSCgvZi'+.ny\^PKXyN͵.l.ײR}l^c0/2plEp&[پOF=ΙrukYf•k6 wkݰB;ef^U6+Xmʲ:\K l+^c0/2p׳!Ãl;iYAVBCC[0`64",L^eqm۫M%s'ftIHj.i]M~Cd-5Ζ$?Jn5=$W#"$x.oʁ '}IIʇ`/}L[WW^FΌEr&P<${.ZsAIbQ뜻WSzFFEK˜Y[帅Ɨb}/"[J/eOgY/T)冺YWt˔C=Sv$~9ElӸ ?)?Rvz䯼뭲ݑ:KwNRHI6&LZO!KjU [^},?{TwJNia#hVfRli c008 lisy|5.eOg˶Ӛ%%\>YbJ6 퍨(QMn֧R V˭= Ҵgv%(9K~},98YdK {e\|<c&X[8ћ?N[Jf1)r E3ڲ,{;JK\Yh+#OJ!Hl,{q;7k-[czxz/Iꔙ[OV~-&ӮAa`p 44 4Etw>]-_ F;Xvu0/{ej&9OVSEsR읃EϖAqwz\dȖj54}א+ޔvUx|ɖS-&*:*OkkW βJN|T\M{ c4ZKwNRå?[W/wd3;l]Co*в%"[3dSXT}&2*Zey|发-|4[V]6:&]j'C 'ugG>7_ť+֞Ѵ/{ʄ",;'lLyuXqW_n5(+o~O~@D/(x$h/ExG0AMd5Ѩx `5&R}+U9I홞K?zʒӧOs>gB"5S72D xKA1O:FO]҆S Y9qrSfB͔bQXGؗ uht5{RrEytv$Lpt)JpS?%l- X}!̵/ ,ٓ ps9o}'dey I}ٍFŒ9ogMGq7oɡusZ&&j;i?'e9+olw4پ] ŝ%e@o 9+xKAJ/  ;?xł4|[,ϗsr޷ 8#k _^#vylj%Y@-!dԨm>*:ѿZDac>2r;';B n ޒ;GzKDj1j;so)H>l.<< eغ(:!?frdN6ܖo#{t*?113( )#UisMf%,c$A_yE[LgjWОA)W\Z{Wu_y@Ԃt q{Gq7oᝣ b+==֧SW(Ӻ>݋ )Eu:oO{eD_kLsضْҋ7YjM4;lSr8%5WcOq41)EyW^bw*jn u;Ui\|,Y(um'#YV֨Nbh>b7Ѧ~_(Ζ-9s4oy䤑r ӳԭR٥&V lpAd¦ـdSZ(z>-{N[IdnZqȪѦF*O/F[å B黎K4|Q-A޵kWSS_`]vPNmTl4Z/(vIƽtjuekkkQQѓ'O\.;GcMqq+Wkh/lqX+SRsU܌Çw9oYs;r;i㎽&vb=qwd}3wgX;T5D/Mȟzg?݅Ė}{5m=%111d bccL?)!!aƍ Mޒ eᮙL-BPE̬-a;<7sVԽ8iyPKd/L,^HN'}(*:ru_bIާ:^w3} *5ڤsh5˗/Wh2U4R mB[eFsbR6ZAfHS ~~#~aIF4WW:8c4DTyOiWnFݻ)Byt&kw%{4O/HiLɜ2^̵/hStXIߧOA  o{gKЧPb2qzTB<(4ZxKL!iaO1={ZS-$*&00hٿ6q*hMߝMk ޔ_MOb9$c6l#-P-o)duߒj_rJ'o CB-~LS=|b_ JlJN26y[-d}^n} Jo)d[j$|g "!Wd:ͫ}cY`?4s/'-]ɺp';؎ϧOz@GYll 5-L&BtdRәdU٥&rUwںsn8mvFּ_Jk+\юB􆖟Mܘq+ ^X}FJ`T'tך7lC|/ىQΥoPoSVߴxz:/z-Lgf/ud3; m8Јl ?~8Z}PE}LSL(o8{!YfƬJsv#Hƥ t+u o|[VvR}SHak۲6TG:5 dD ǬŲ ӟrE쫼$cC#XzGq%?WyK[ o|[R$žxe3׾kil3ddI?pNB-8>{ {ikR@08UMzzK}=zRdN R~偽~ʕ+=ؐX-kw/'22rŊ7oT] B,%2غӗ[褷brثܵAu#?jz5Tb  ;?qX(N-߾5dlثgM6.⻇Z,mF[g!OЕoϞ=7lp~3Eo<_&z,+172pHA)*&Gd ~BZH?f͚UQQo]GR^zU-BJP$YSZIƆd7uOHWq}KR677]F9os.2ۙ[JOI0 Ye.TiĻ\+ik{R߸F[ZJݼ4DKk~k+%6٫=.jc+N}åKfg?vRJY Qvz悥.kXWy)<i+&M7w;p޷t!4򖶖RR#~Kneˋ+y7GFڞ8tĜ2?X4;fB 5-e, i$M[*(9L:gieg2mW(:vԶ,(q"[oW5J%jBnCF?Oy-U2ҵv8R.;۹)*_rnR%ź-7ronZ \gY!2RR#;qYd*,>dyS-oYRLm/6<ӧn Z.O[3oR߸F[*yKg-!J}Bt[-tΆZ|adn_JEO=H}&NAt5%N|dIʠ!lFRe={ Sʨ(KZCˏ|G eغhuG|JwXNdp38i{T!TJ,F-oy5l{b"7V[eX"6wb^+v>5y\:I"wB u^^BKf㪢cgCBN~,u… ZJu\mKSԭV|µG?]j=$Zf ݤNr̦tFڋD9K.\6gAn=د\njΒ\Id2Bq])e߳;눨2>#{9Y8=SMk֭[R=Cu7r\B !r[̊{߲!7ro'%R1ped:a?p\6pu+7#ܹs -Bom7y|[gjW娐ϟhmĽ. 6pHB^}8[׶% 1{Uҫ5X=Raݤtu4J!Kz!ۿ~`.ƻE\tew>͞6^аYf}Sw/aN0\Geѱt @I<^>OIJ w=WL=_`st؁F)M|[]861@[ZO\X\zIԡ#(Th8fr{ƶH>/DgoII ĕzKwp|[B>֞j\ꇕ\Gpy2oMoɁGy<.1 G: 8ooa[ Xu7ӧ'bIL?i5r"3sEEPNPE̬><}^}|cO5=%o 9zKۡݯ)%!o!o5 mC'X-ɑwzu~#;F[¨nYWv-K l:-#xK&%rssH(lGB9llr*؝O>Dŭ7-9/xKȡ|[=|b>!Sb6C=oYyrڎ; Wf?Pl/TO^Ƒ{K&䡓Cd}KV^Ag\Y}5ilNo9nEA ntHo٥&Zr6o9 <[r_ClhR""^R~J9TQw |n1efʡ}le1RqB WA{OߢcmؾRa(Փqo0Ϝ%m'ڲHIIfL?\u\S+]  &W-^ J$7J)dE)-#xKD6e'm_6L|)Y>مZ,*=[rx0DBCqi:kBk,_wKo#q^(r/K2;~`P)*-#xKĒCGХeXx~+̞G)!Sa#nwz%zN}-aa=L͞xK=CROߛ(셍Lh%$SQP}VT7ݾ{K.  2Ko#~ۇЁU)킃{$9?<^> 4<%%XKIdoi[B'D|`3[3 QUIt>e]Z,'͜:Xgr 4(d,Ξ¢((<k zK00x$k%-#xK ޒC=Q>fsZl:E!V9d \l⻿A>d?n'Փj[Vߤwb@oi/  2[G%v!_Ė<=/g!LfU"(s6"$9T?*_Sa`H84-9Tы?nQ)l8ڽgM2`mTa$ꙷ.z)'R+2t*  2[G%v!QٜQ_]#.*b)ejUqT+_`oPw-1t*  2[G%!zµisZ6N~ aˎB|lA:J;1q;앳<_S糟S.MCyK%a`H84-9tZK׽d3PHwQe3uDTGJyLmoZ_.83-/MHu*9}[ʇa#AF~#xKҰ3D<0MHqQ >$%n_>"dCaZS}VzN]hXoi/  2[G%gCGQRs5u舐PKXx~+̞G)!Sa#n|dD~#xKҰ@/xKȡ-FaoɁ_C[-#xK ޒ!-!y[kXthz{[BC!oC xKr( -!xKr(xK -!xKr(xK -!xKr(xK -!xKr(xK 6o8<ȣsQ[%P xK@--Z[jo %P xK@--Z[jo %P xK@--Zh-333>믿jw=h-}Y*gIOO{t0&񈷄K<-9YZZ]e֘L&O;?ر@9[Rw o K TaϞ=PS endstream endobj 20 0 obj 30997 endobj 22 0 obj <> stream xXɪ,7 ߯u;rAS" Y2@M~?$[.%6g3Oag%}}g޿u?۽b7ak?1^@x s;=uOǗjg9d@; FS[JA+J[BDX'}>dh $z͏!++0yQ$d MDu5("T,Ӻ&L+88UIs[P6Y2DO +*cy_DJr@oefj~c4%j'IB_)"!&O OaY,*-ڭi8 n9Xd(QLp'a_hUdgzE e1֐:I'-Ki? $ Jz ƶn6daZn2fӐFEI<=,Z}S16Y2|{?!;B0nZtQ>hK$.o23U5(AXv!vYЎ-K5R+Xo:57X(> stream xZɎĶWl۬" h40 |rJAS By@-Z_=R0G8Lo3]gs[Lq!LpWa<g y1{#.v53t5ħGHq3o@c>1|8|כ ܜwdo.ב?Nts|Kts\?>߾,S`iD)TtYoJ1?[$9r"qm% 9PDND@ ^ ꈘPZyN#<$0:=0qn w{'|: ~e~eqœOGއQROS*fBH#a5XJ S-A/,KXKL Ӝz_Vddo" *G U' t80CuXjM'ڌ)hYJ.Mw|}KS$VQlпD97~M8- 2-d[Gn{6ikaNxz@v۫F* 6:)V3 A;Wz+Z۵[&DDpEl6&qj|>$]~Envgf-]g rUԱnw[,}躮ŦqЎ旵M{ڪ%CY.JY2Q %1x~Jc Sŵc SDr`IxDb:CsWJYXg ]鑏XgU=}ϳ"~;H8mQ(_:<'Ja}ȳraOKNly%"jlrXC|("d\1Eyݗl*Ẅ.aZnsF U^W4ʼnpl6wćq}TQ}gjhdyNrM4t~I4]os$`,Zdy݅= Ц#WQך.GK@~/HkzaMm_z,!t`s6TN1WGv]Y=ݰ~q꣍qJ1^7rb6 "sK'YnFO<O8X/ endstream endobj 26 0 obj 2244 endobj 27 0 obj <> stream x]yT~?sF\`AY$" (F FAqOT\" MDE&&E%,ePqDžhby~vi|ܾ_WuWNMeI?'O% ]ϒ%{L\?q7ֹ131gt>R ƴ&cZjĜ`LK-R3:icZjsF1-`LKmb|0iMƴԂ1-9ZδsO3ƴBwu܋o,YM?Zn@Y^/3i)cZ}-܊B0iq?{ [(nE8dEDFw{cԂ1-0ӻ toFŭ0Ͽ/KWzϟwZ0ϴarM[پ;flֽZ,oi&O/l /l]T ƴpLaYf?dby m_ť֠Yƴ¸L[\G}|;ke6 Z8 Hly/_ao㋾0x˾C nv79tBfsxd Sg.,TZvgdN wrd2)_a}v3{zmdt=P8|;⹱ƴ¸L[xrDXѶm[8šN~eJOǢV?lPDMiewj}w{V5>h%8dǫ95L[zLWqNp.X nZٳO?>*lm`LK!˴VC$įR{_L "ey/jZ I38qoJjzE@>h[2y|;f,Lͯ:~+iH^ ݶ]srr1$#WXf|lĄv2 -ɨ61-0(ML:b콜چ,-obZn9oU U.uUyДxXvsPBB>%MO  a9R*'ބ7~,-oʟ5FhERrVHXTSG2'61-0y#|}\I㨷/NHQʎL%$?.l'Lajm~uKiW|H wLZhR1-@!ǰf \P?o;TX(neG>?w=e dB Nn&JU0Fdښۏ{25?N+]ZDna~.R7[igZ榭Ϸލd\ktJU-fOO0f$o]N8kR(by~kC mcZ aDr*pJ0''6:u?)ŭ0%Xk<~ONO@?ˎe2TAg)gT_|;~S5j{>@ʪڴi93W90 $ӓSڀi=6',&Iq݅Gz4[\j\)/A- ʴKp!ϑ19uqTӱhy<5lܝKn R |NAQ ϣMLKůZy mh3xn-ssr 2U&~MOޛ:K&הȏ+%q2c=kx@ї#uq+q_HX[~!zzy۟ \8}~BCACgnXWm֣wjǎO,U1w +o;XLp!ƤenJ6-eZ Cb'k,Yn]_"A8>=n5y?ԫKObU}b.5b2-A-\i_nƴWnllTuLbŊ_=))Dr2$X}o'?d;dK .%L~tXȿ=Ph1+<\%cZ`%jJ!u58i%u/F<ڼW{!q-jKP ƴzA#r*u58i(W 4ɶ*Τ5e (0< "q 1e(l%cZ`/Js] gZj=B{r=#='"VaN$&8/!GMؔoKZԖiݙVrYtdZ4UHV|IHܼ_n+9֣C5?t-Yd#qm)?$[ M[EU jV/UВ^}5k֤~ |iVF<?J^ѹmڴ3`X$~PIvKEGz0$$EK jV/8nN+3f߾}؜`9 `·5$…*ܚ\xs?/ñ2(xg33I7v@PԂ1^; Ks] z1mVBBB222 -!Ù V9ͪ~2]X- 'NUKP ƴz^L+E\bjp2r{uCAo0_`w^xC;)'gy#VKP ƴzA#Z%X.u58i}Hlw3tXGI%Od /5b-'w;v j´jmhdEDFwosAƴaaa V[Z؏=Up(Ӯ\R`ޝh}J^}&Q 4{1T灤&& RVo0LP %^LKl1o="Z0ƴO>UqLO~إ0݇CP믿4$54k%멪W?d/(Ίؔ_Y_vn͇gcY6=(U %^wMf_(8@_b]ǎcy)~ɡ0h,˙Sis ȑ -yRx-A-bZ ˫S}0-G݇CaP%BtpXY.a#6{zv~ckQUZ+ U0m=`0.DD''m&&E5a݅k jn?N=+MhO?~w`: ]41y=?=C<|"_YacLZXoG݌UAq5 Wam׮\*Z>x $#W*3>i6$8\5'FqjgQ[-u޲P4(Sky<ӊ~;p5@Sm]L19l+s;w !Fc2v{@&$پߠ.2yg.Lkf|^XYmO!#-?~_zb R I՞cwU-RaZMNRz-'Laj|uKiW|H5;?!y,ohsq?-Q29:wmuƴڇk6 Rگ41-p>]GOVE9Вqȭ'Kd*U> stream xYK69@WDʒe`\ v'd3˗deNn[#poߺKc:aWz8׷-cibJNcD_?~[Qt= @H XpX0C,C,1tF<C99aҏ5!>s;C }HCǁo˔a4sLfi&יݧ?~}},yiD) zRG#"i# dK@>dRO!3^C<0F@p]ȌKrGeo* ^V[n|ȕ ʭSJfݖd@l4D*yQ@J=IJ~3gHlmDU䶈|x N U)KpJڮMt3vGo6z)^z(j-GX)ZHv媽jaJ"> \$Ȕkt$ y `|1ETVgC0+.Fӊ%T,M>ei5>L MfqkY3Q2}e}zȡR8e |VfOT5垽ss%LZx]Qmbu} œ&5`rB7@Pm݅e[MQ4ct,rNZ#3^zVǶ5W:L4uF}2ffN%YF, ?`jܵ4NJT3͖tstqއ%*ZU%NtSmלlӧ΋}$ ɸ| DIyn>:̬⠄ᢥaÅ,/p7l@?3,A *gB~7ҟ0=ܩj Ä_"jf MշeX‘vb"DFR1)TK7d;5a$]INÁ,n'YrB& D 40v$Bx*Pj:f[ zM!o zs*K ]1m.]+Xz] FٯRiq*B*s͑b/sU@;gO :#->3ȩ5> stream xiPoս_t[5;".kh⊸ $1&&$=*(( (֛}߶9Q̙sO_t _0#읁vm _{×kS*zZb? &D25`````D2Rɬ&>pJmB$X@$) jb? &D25`````D2Rɬ&>pJmB$X߿Gg1 2͢1f"NMdV?L?z >;fE 0`"MT@XK+=~z|n*_B2,ۘWd5?%C7B$3Ed"J iߡSwJHF/tB$$!"D2ʓ}U:óoNjNP1^d$D2SHf[y*TBE䛬X0`ȨаA#RU5@adYsV'o>CZ򰜼?Xwut ң稉SvԩݓRZEwK˘~u?MT]@M 5%k~ $↚ۃ) ̓Hfl+F2PEPa&s &c>YU݃$Eˍ^˚ͻ>l'Wڣv9#3l2w:;5?db(`!B$$!"D2ʳí_@_}UC//s k.VzsQyמz{̤2_??^Mʼn5w)<3{9 op^Xm䒋MK^7"m쾪s<fsS֪  ŹyU&\ XH&eܐ`!L?1dB Sd-9HSP\!-!@2ɀ/},Z} k~MIˑz3f/⪭Q"4LÞq_ep*pӶܭ 4p.bL2$"EܷCX;H&i?ӻr4os6³)OeiW|p< B“?}Ckx\죟f/[7(8'7L `2W46iZ8bmfم;'O亮6?|Ưj㖸 AЇ7O>r&5!Ϗ[wT i/&(ʚE2y(dCmCX"~(h\{>}0gA%_Yc;HF2wkx~YWyC|"wM{qdQeۣWokY|}z H,Wmebb4]/.߬=FhVd\yB`Q$+;Fk4q%c_uˡJ"X y✼פO9/ '^/Rr z@6G2b4GoN=C@d=` H`dbwM"4? pZmg ٮBrL[✼4^]ztH-DɗH*tofrVH_!`pWd`E$9zU vs>•<ɚ]@\V_R(+ e`|_z@d(XzZB$DCH/9g``ZtWj}G]-Ǡ<ԤO˵&}`WIr H5?%D2N4H8$X@E5ȌVf/g}g-0]m>!/VGe6V %k~@XKdhqHȒQ1mē֒^ѭK#WW."}B^'&khWlԭ*"J 85dC听᳸ M]@si)siA~ vd7$F>!/VO]!ʎJLH H5?AV6-Ɏ8`9$kp.'XqB L\a#/xeGA2AN^[O\v67eç2:-LECB$C ĚdKk8@u~$WfǢHf H"2,g#sʽ`&s͠`(gM;9y)o>A0u3uKo H5?(E˦жK%gI +L"'X,dw%߁ {c̩ߐ `jT#K,#8)Cן?ޭ&#dN^ʴ[LL`1:vc|YKVﻡd'Nعs/-"){=|oFH].ALTÇk׮} }! Y$l)Cȿ/-[\p!%&죏>:tl}\F!!-ZvcFەW >* M}S|-%K57Xa„ _cƌd3MLeg˞Jb583pHаа79DʫK9̫g_^'na"1]pQS .C׿&$$PHB$3Ej 6eM nH&c᭼Mj=~x{,|Q"dzy2ɸa3%SYu fH: dfL%m *="q﷨z[N$ゆ")h 5dnH=)&%gaVպ۶)H wzi O]-L󲼀||!j@P*g= y2hL.'z?CIhX8P8@ɞnPo+2m)ԕm$_3m4({o;\@[KhAa#jXN[$a3D2HfE26eMf! d!-`!^Z"4&ZZxd D2Dbd2l!)N,0u l9Tp#Ӗ~L%m#)pomtB26C$OdH'$.k2ɸHFn>*Wu:[ #mb\H&YByE2.86[x1}呬Aig$U~_H.iK?yD j`9 ?gHfR~Ŀo8;v,++7 7F2_Mz LdE/o>.@6x.=w/@\Ay΋5m4<4h۶mϧ< (H,d܏*ҏm\DIGLAA~~`rAM,gsrr.\ؠLNߒq$ᅮ&8od.cf;"W*Ca \+Az΋ed={4| %_rT6YqI&FZ΀o566&å~B$3E"ĸeMf!͍TzKV\{yz{5>XOd594p8J$'[_%kw^,SLLb\hd =kdcJ Ш WXo?|zpNAn4r!ĸ@$OdH$!1.ppYHrC- `sD!LcP] "yKA;NQ" y2dH mLYgm~L鑌|Eh F\d A25dnz YIQyҢ%y1{<-@yƜfv(Vr_˜/[Y;/HFCb\hd=G2&ǔɂD0Qd 2id͒dI15{rd:G@`X;Ӭ!ʕ+)I * Hdѭ#%7Kb\d'Ow8zZR݃K4ɜ):=OO?vuWk~ 5ݠ4j0=~\L2MGfMBytCAA%q!)N{iqn^dQ/1G IP2"m쾪sÁpA~Zr՜S{QHڱ}N0HV) 壬v\&#͑Ys;*)I2`=\E\jwԁVJb8^*աbLMgr;80B?sKh}gr$''e mww\P\!ƠdQ8k rPQdfq.]d8XwUPmoW^ݡsգWoBZ L!̭ f5?hd*=K,%/q)pQpH AƔ@6n{cdrJ+]L]Xc,2=9$㭶>,sXdr6:x?B0:IWPbkS̞'o>n ί gaችosԑI 瑣It`PP=GMdf >㫙#U@2 .$@٠ᯑ9rlIHV~Ù{yj>6 yj Ϻ:5Ge=DSQ6$E]{zC; %k~@XK,#r!::_s(k6<g;M#cu\W#Uj.{&Ï={6MIHVvy6yh >C׸:ŵ|ױZW-)K]΂\ H5?%k!rB}m )T_힊nPo+2ma 9?x%sZCGip&u8g1s/k_3m4(v \J2 ooPlM[qwSv^d VpJ*\@Mw%f먴xʡməF89Ժd9S0ʝH5?%k!rB0u ߑ_-\&x9r|i?)/[yEDEs%djwqkTwRdp& `qO'" Npx(w a6BhnmKSpH (|!EhCLd(XzZ5 '47#ٮB.L~// )}m??r?b?|&٥GOxN}(eE$K49r&@ YIi| TډD?|Tzم;@d(XzZ(!'M͑Ueq[ɕhݶmG498{G0ƴ-I*QclWI+Up|%c3 F%!|zLPdQ$CNp5#YVv-MHWYqYH2jw?ʼYQʊH/OGeLwժwWhl]9/kbmH5?%"Y8_5N`H_*ۮd 'u kF-bwRVG2PQ%H4{!*,-Ru+R2uR(K#(=!ݣg Ygq/hp.KP1ih'x;QyA͐-ULD2Wg=B[He$ĭiSHf7N0ܼ[Ob'o> ouW%?>W%$SY;x7ɷYC.p*;J_R\{}@蘶P2yny)Q3 {ۇ/"!\a#)w$N'Ow8YR݃K4ɜ"o"$3 K5?0db{gTԫܙdRqjI%ŚE2e&S̞` w0ppHI)\yم;'Ox""*W$X|~WVm=6; cvc[V3Cl $H=Jy"'s7fj\uRr[46NG/͜1.ڂ%:u:&sw&"љG vs+8ߠCnbt g~h@$$yy*ٓ#Jd( HVuEkH*//MR&M_WI5$нŹy'o>1Ig|=WCd(x!VvFC&̘'09q*c-Qfut3tBW%Z5?{p$LqjSštMܷ ̶'m9T9QdMa刓e }_ 9m2x)3trbf4D\Q`D2]GB$ld[b2cwXZWjdne%mŹ9AW1ף{R;Jʼn5?|PceH>{a?d&[Hf[ ɢbڐ &>)E&HV_VX 6tcxSZ5?|PceH>{a?d&[Hf[ 53{9}e%ne%7"*Ś >h12$S`D2]GB$ldžݓR v}LeM̭̿PX R?<2J[KjL g6!L}kl+[!{߯\9[v蔳f%5A22JN|AW}M&x;𦴵k~0$ʐL}jštMܷ ̶q_:pHShx4A22LнYo鼼((C2٫ vkA$u4q/D2ʞHFTTs)}ʬ3g=ɸ/U+9^9];u6j `A-#٫wkA$u4q/D2HFTu+'{reH&MV_ ݃NRRIhL;5 :o"mHFT=zJ!871}_ 3ri)AY ٫wkA$u4q/D2 ̕!dnb̿b,4 ٫"' !Vd(XzZB$$!"D2 %k~@XKd$D2SHf[!bk ̓$%f\6ÀWdf=`-Y G@$ɔ*k5`````D2Rɬ&>pJmB$X@$) jb? &D25`````D2Rɬ&>pJmB$X@$) jb? &D25`````?hZ6!=`02Zy endstream endobj 33 0 obj 9614 endobj 35 0 obj <> stream xIt>φJ;4 r3 r!_]U*-oQόׯI}W#~ }_o pX?ϛxpGp H ~ILk6%çB$J'kNi6i0pJ <҂OSZйfZY3 q)͔?aܣĚ\?1ǿ{?O dqƟdk:?\M>y}>xy5A$% x'p&gH! Kv0 d$EHW8 H{~DBS ;:yX,x`Ũ"H\&Bˀ-J JMIF3U  B(P[]]V # KC^R8HKN65&M!`\$3ĥh%Ih=T,6攊]8PT/Mtzv}-0\ x-ǘ+PMC%N?]8HAKE(&ܖ]` Vhդ춥̄Z}>E 0E~텆$…4'1#VzLTC /:p#A;vjM+*܅*<~t/LI\g#')ӱO 6&a7`46HGa!VA] ̃SPfIz̪iZ:]Q3þޝ`H 7+夭ܳP-LLVumCĺPi#D4d2_4eⲧ kkjkv~Tz\:C,\>_&MvzժOARO &_%)SѨwA;sN/JlH~[&B +B4({% Ce w.iP@yޜ]\B1a"CͺK,޻+;[3P2Qs2ϠgϾyϱ"3Ւb7yRTQy%fIH&\7QV>l XLbJ89y^kncJ (XUYvnKFǠgLvqEgLdQ5VfIUu1@ߥvkU(]C ;ζzGcAjƣdqb"r#"PX8,ORovuMD7R, 'b_ Mh³ۆ`0xICw*䡘ƽ`UyǓMC"˭g]Lp p钠B$iԶ^Z|V6eh8ӿҔmKyTn o $=g @/4鎅ܨԂQ>4aM 5vC^9j`=v1VI/s6W6"~=h#5`tUow{IG֡QcA'.Mk**Յcg++Fs%\ g$0%__.,$s#Xp w \kMfP/䄑<<Au|'ӕ ƳbىI{Xz7ʠA(:+;U$5&SL{Wځ3\ʙUk+ή4j>᭟*o;~o%5{-^r=ff1__zmW7]/|fGi ;*o;~oK..#y8Uph֡ ~P5M E,&VGjaoJa&)oIJy|WzӅaXF/V"BdQv_1Ecmt92mk9k0ϝ̌@ѝ' endstream endobj 36 0 obj 2539 endobj 38 0 obj <> stream xZK$7ȳˊ+EAUV% {X|/,f|w($"c;SR>~ZƓ[ֈLTQfS||$&~# %p٥$['oat%Q߉ա[Hzg>=zyE]3z dfB g@S6-0\f'VuoȄF:a7mݔ3盭fՒ,BވD=:0ɲm*W܉@Aq $ L\!VÇUT5+$VfzmDX2j kfWㅰ/^9%*?%o鶾KuCc3KX05¨:LlTeZպ *zos0`ME1Q޸,$LpT.Ul͜ iSɑgȍ-qVb_yWv *41@֐TDj2ИJUw}Nĥ_٠јzɵ5VgRZ;NJ!YWBK+v;I.t-DVuHk7(}_\hMF%WbXJ&Z+!\ ~:"w=Sn1",W4y?2+ؗ1;1;"{@JJQE]CZ;䫠b\_vzj#gV%9>mxܳ{} QJq;+0 R55pLR[TSdN"ziiY `f,ٓxQt)OP3#+@-Ξ=BKGzN~[`J5cnĵEۜpb%{H8:@>(}/i=ڂvФ+P]&Kͨ~s>8s SC>UtV(H)h?g ES~@ZR_=c)cay2akS:Uä_2Pqa2le=t~!'«T~dq}a(!CPpq6gʫd+GGEBh~kb:}K*+E;g|~QF0ic< u {$,ےAjOdYP'_i؅CX0l]a8ι+9ۇRG8);BuDeE)9HF.srmouj͔tPtBII )I#]p9lg3>}Bv@ظh3xSeDfD( m>Č >H F N!֖!wTȄE ;}kv5ia3ScpyNՙ5nH%}=lW>867q;g?+{ r>y4 Lyoϲ1G wH6O$ (]RjN /?"ne _|Ist~%wx|3&9Ys$@P}u#H,_Hd관z~}Z_\x endstream endobj 39 0 obj 2273 endobj 41 0 obj <> stream xYI$7 s{,yWHnr9%3$\#Yjq*|,.uIM~hO80}t韃:EM:Xw7&)NO4iS,u0dcI, C}fȫD?wc}c[0m;EЖLI?'3yo?/ CϿ ᬴2^\3\Yto/G{VK8[zpOG_ϷDžj,ٷVd&|!U6xg0&_)qA7Z`$Zd!(1*pxy{KBD5j%^^ݔr ꇂ(aru&ܙ9Pg !r0QDŽV}aw@E)6&0|U-3nqcfO z8yB*%.ͱ4W3ǾM7 "ʫp^}՘ =c[`Uufr,/n׫# q|)Sed.)o,;A5n78ٯƹ\h#1yo P~R礎B9ʋܰ\)Iut{8BB c6ºH"h +95jٟ3M{b%mS䳒xVY:`[^`Oyfn^_zSRH·lsd*GI f6]nϻ-e{*"bwVcBr:f~9{AT,l]srSO/"EV ,9Ω'b(ӑ^uSax)ǮT JR|~|>wMT`}=F&Nvo\{)KS2')XtooTS)0G HwhZtxV#Ȃ;ދG=FD71~Dk]

r\o}Nې&o Ɉ f_N,_ endstream endobj 42 0 obj 1898 endobj 43 0 obj <> stream xypTUڇkf)ξAH [E "{ T0〠l[ @$ F";Y":ST3s=}t_ҧ]}z?}XtB=CQψt0=hLӃ`z0+0= LӃ`z0+0= LӃ`z0+0= LӃ`z0+0= LӃ(ezϏX,c! MxD```dZG{,k[ɩԮѱqAA1q)2'MrR<LD+=HF:wYBfKmRwLM9`L1yo"u;sgRW]k7Tns3NBmjh9kIeon9\;ߟ]އ2Kqu0=hQncI1I)-bkn"&m{MO սuto}%vZ' /Cf) xgzꩧ~zm9\A3!w6O jzeo]QWz$pj{NUw'.ۥ:4ynz+m?wV/;73ި1\{bJj_ Kqu0=hgz"Ux!ұSjHhkߺdz#Ѿ+^spȈkwU5~:4(bF2ޑZK-Wq?V sAk_?_saKNJ>S꩒Uuaب]WZML h>) \ R\L s-0= LfC`zY!0=`V`zL0+0= LfC`zY!0=`V`zL0+0= Lfŀw?-sѯ0=={@yfz@)`zf`V`zf`V`zf`V`zf`V`zf`V`zf`V`zf`V`zf`V`zf`V`zf`V`zf`V`zf`V`zf`VfzN`Gy4`@ӫgD\x)0=8 LC^ L!N/C`zK!0=i`z40=R`zLqP*1ع>0=8i^T͞is{~f;ѾBǿP$jضzmy \ǁ>0=i`z LO-k8K['d:6.2*:{*1e'nķ9ڣ摑-_K#L)4,,9S]ۊ_hYQ-bS:gf"umɕo_JŕhP{QִY1v3yW82Zpg;عF LӣdϜOr;=kwތ?vfxD3cnF4knnoaGpH(!88d'ۭW݄6,6Y@yG/ƇHKym[Rd-e R2e Xie iwkp`-0=M_e馟 W;y J;u޽vY 3 Yƃ .ZFs6o:w79/V5~_\0DP2'Mr 56B! .O4aTؖ K/r5Wx خx|`z LO-k8<4=:R63n~%zƇԟiO8[bִYRV6?eN?mϬ%>sɛRK#%Av?1x,6-| kvMk@ 3M`zny \靺To)~΃ܴJNQ{R4ag>ŵ Ԟֳ|1yh-Ka~zrgz[vkvM\Z`g;عF LCYVCSߒn;jɳ5i/ݡX?]Ilz-zo= Lhӎ$0(]!ĩ/_+_3=o)]*M 3M`zny \齳~+ux!3n:͚?rSUx xn|M˾OsCdοKKa- |e ^k"^e iwkp`-0=Mav~Q-b&aW<v#-Xպ]WNWr,Mpr[?/]uRn.Wx خx |`z LO-k8<1{74OIβn• 7,r;3[tٛ9?n5 k"4=o)]/L#tdlzvCי5^1=鮹ctL7n,0=WMNkw˾>){Giz'0pw_=yw)>(8,b+9{OaRp);/7N|Mdt K7v:y[aY>9~Oѫ>#`-\ת߱!aԲ֖]'M|) L@l@@>0=yt_IF::GnS n}==;tn`볱[1=ЩvܔUaZ8c܄tƤSŵc&pڞPb(KM.^T5` k@ ˦gt_ u'%p۳ ӓ-)>N퓳wEFE1!wCWjݟf.y3g:8nbqmus̄R Ȝ4mbA!×^-1sEw;j<⺭X)KZHhƨ |%5U_씖$]*{=^tL:fknyMҽGGd58v0=6wbJ'_+=pҤ}ѭWv1Nwj^б[l/F  eo LOL x{x]5`¼"O?7W߭W?j?G[^cG ӫ*޸)3<[u]eL8< _y@B&-63K~&ǡCMk=NęSg2_H_3=I㻇י^uC:B]~p]y{˰4n/x FW2ro~P;2*2N77Oqs^[Hfγn*_= tc LCr{6-/Ojٛ&fz(7ӓ"_w㛞 3Mr/EFtoppH3u 2[^cGUQZmȾY 3"t<7<9TNP|㫋ѮnAJ>)%uM_3=IqZw/5c&l9\aw;nmB~kpjzܕ dzJ7v)ㆳ 14=ؾgRw1efvggsk}JFOE)>nέ140=)"gd.ӣnԹ-6#&Ц140=)g|Fӫo aYrgS ߥFXl~6H|k7u6x9yEUJ/ܢ2>5Lqi LØ?M"\(zJI*`ȗKM=k~G@.ޖ˰pj{u8vǎ8\Kㆿq=:Y/Z| LØ?Lύ,nJPsG~IA^kzU7c 5t纱c5s[N1NmMG}]b2zFr۟J Q( eM_~=1MhǓَ*n`볱vma׿\L[ȦgLסCzQp\EY{gV^7N={СCwD禗tҫW hz)]]%zlwX<͛7/;vÇϰ]=vbccjwa-ʚ3Kt_Ŵ4 po] ?`?`^u'ִi.e}W~:3pk:un}R~wÎPOppȪOQʚ)*i,5U] Ǫ\mͭ,M-j5=g6^b=\RR`znyW[s|عFڦT40=OޓNg9snxկl߲u[j7eSr{fvSn~GڐB?Rzú];ELl˄nW#-"J7fv*C'Qt֦u.Hgچٯ iiŋP%ӓW>?eާ?DvՏZ._l=Ȩh=*}'R1*ܭ4TfZ63s4jrQ6'ƁFH4+5LO^`z*wؑ1= NsLOS44= SD#;֣'EFIʉ3Z]5qW,^̚6˺#5N9ϺBBznN=o 7YK }7/BMOZ4=r2,MP ޥ{ ٲƑsM-M◙V|٭ PXxn]~kYWT LO-?Y@p?O+W\phѢRUc>z\O}6俕znt>=rºOrjWjYV#}>{Oڋk63gŋPk~ŋϟ?_| MB!ͣK\woqGiu#r̴;nm}4^Q#0=}D8|]v/jF{z>lffC~W+!^pp3mx)]?Gi|BN|6 ~q{'EcW.5rajpDm/Bm#زeˣG\" TqwgOT{;nm8Z-zo%Mа du{z wؑ1=N$sLOU7=PDB3]K܄!֍î {l݇2k˾1GUϽh[{&Ԟ"w酅S wDZKI/74+MOP$MhwL׆v]=v:zg ~yfVTFI(+鍙ki|يuT*>06g:5?)=F-;Np?/I!Kծ++DOo}=2=_*іQX[~(^NǡgnY>)%,<Cr9\.?=1:&Q7RΐΘ{I3y$:~JtƔ)MG}ʤvG-9{c.=iczaީƓqwk^ƽYNN}SG4Ҏ7=ʊuтFM~7TT}SZ:h>Q2{(-vukph`znYlqQYrbw*})+#5)u¥x1az1!ǰp=5= J\4}-t{qeMrvLБcˍ8 8C>ʚLo򕖦A']E;a9QP;M[|Cٚmب=ܘ0=!NE⿽e*Zpkpgzg3S.f_k-&r<,fȽ}fQǩ{q@Lq/2zlT2= -58vop7~t<,'#Ǖh4hzpQ0TQFpݩ\T4('z`&jIћ^O}D_A8 87nT2=m{hG[^cGOoiD3sM"M9;3絅GY{1azCMig5N4{=T-TgEbS1HhSNR.,^i-dnL@0{hG[^cG޲ߛ%C)m6{ԓ;7&L}`z{kz3T-y%OQh\Npb 7n,|zF4kƄq@Lq^kCE;;j_O$u,n?n,2SLε4]:iź*t؜%}q+-8$]ۥ5 L44=tL׆v58v4rrtotl '.K/':)4,lWYǡó Ie Rol|UYP NcJM|mhG[^cGӣ ^ 5m%RfjgnY>)il5<ߘ0=t+EecM:*eL44=tOL$ezqhY>*ܸS =˿a$tQNCkEvq+A&5=;عF LCw~~~5.$r҈1:$w KN:zm%~MWbZsKP2=2J L458vs%Z{;vփڶ{g)r~2'ZM8kI\&7mִYV֓6%۔sY/Keೕ iwkp`-0=K4.;9Y~'߮aԲn'Lه#'3d)D`z.VL`z LO-k8/QvN|u2Ύn {ErӲ+OU0`z.VL`z LO-k8/Q]{nYw'Ί߃CdAAܴu?vZ?:6NM Rl%g;عF LCn|:cv]bٕ+|t:KVߪm{֓=zDn"_0=g+A&0=5Z`zGQEˬGcQ;cl=z*ɜiݦ/\~qGk/Keೕ iwkp`-0=K@qSf=<8$t\ΫE{>[;4#o9 })p{q|U]3}G9~LjQu鉔VL`z LO-k84#WsOhmiF=$-,((Bډ/nA/df8Vuڷ[W/ Ƭgzu5ɗVL`z LO-k8,;OYڽG+XmEFYlګ{>vOCk|VL`z LO-k8-Q7OK?$4,0=i`z}`z(@P!NZ ѸB?0 endstream endobj 44 0 obj 11663 endobj 46 0 obj <> stream xUK@ Wy!yC؇=޺C{߯ǎ7nK`bOO2h~7 'Lr&|y2?h;.[CMtO ږ/ʬ0Hķe!u=ELUC !4A2ׯ?a |OFyL}O>ùO9~nksmAH1թ"PfgtO*9 ?pӡpyXŰ#c#ua3vC`FwwR#\`3Gh%OG̘ > E >!,ʼng.pm] KDz"dJ]Մ=,ot&'3 Z?9F+W &ElX㲂D?A_iaYrt<ٵJs:q0u %fU iW(^JRz0>%wq!.!װk;;(qV0*IUmIbvZZwhI )m*K  -{VG5hqw[^n N.kl8 $aIؤ16鑭k~]ݧDC;NGv{R690Ijl endstream endobj 47 0 obj 656 endobj 49 0 obj <> stream xXMFϯlipU'BrrJHh_ гW^U5r鵡pf:J5yzSڎϓk5mkXkS3RQյ9_cnksU&׼iGU&c֪D:ΐT(L3U&rUDLLUHLu[LbY@/'Aun^h>/ܰ4/~I3H'/]8f8.i7q8uϧeZík)SYXӳx2yq͉ 81ር!ju, ly+SBtA ]vY\`.F'G% ;Ax;.2"ڠFEQḡJ5y~ 򞳑2WK'Jn0磛ONS8nyXfLzCPTgEe$F6}4`Vѱrk<[Ѣ S-vDCr ( u@d\Wm7;PDRP `AxQNCsEwV<ȂGATo-z YT'mGYS$*[늭enjW5\[3h8{3 =gv%9k'^:Aݢ6[C=Zgρ;:O0JQ.յ)drh?ɁKiذҹg,>d{u=9/4|L7;m}i^k/])͟Y^o@D]矾n endstream endobj 50 0 obj 1233 endobj 51 0 obj <> stream x{PWǷjjkF[/@@|g41&@1*01%Y56jVE -VTM~/07_{3=>E1g}g_~}/xw?1 ACCCC)ϧpB]{3B Pl!t B Pl!t B Pl!t B Pl!t~'(ٿ~q΅ q(`sϮw Կ~9678?"a׻`]\x@,q.#{PMn;p7=xoz ]%^R]\eZ!t(!tB2˴hleBrjhp[@@iO ,ZO)Do ,=kAXx ]ƀSq1BE] 1 4Q%cRj" YrN@`_'Z'0(xˁcT K+SV}.)c̉G.2 W&j; ed4#A~1<<1B2˴26t}P`+;řsC3fT]hv}"I/LaV_l%o*:`IChYa5.\W;mzTIHN}1C79tXGVbhQKO_N'A~1$<4B2˴26t$pTebx9~jVM". Iӳ ̂JI@i-UmBcer'%`r/Njwhd$5[]FBy:t {yE1",s豛??|c#lC'W\1;6OXD'7.o,ͺ@-Q 9KW0? ^NH\ɵ=BרqoPIs?i<UKQeV~1l"koGxL7@͇FrD 72 tJCc-3C@N:%..h:}!K,a"CIypƴYZS)݊T` 3f;n'ʶC+!>>o\wfEGOoЋ  ^ 4~̒[B:_R]-hPa8ۂJ BB˙2{6NJh iIӊafQX6=k^0S9fmT⋷'̘  "kg2k앐}x}ʌoN ⓒ}|}=bAРSP8Ax:t-[]~|E ])|f[GG{dVX* ߘւzKg[P).te̝(gA0j).Ab Qo-_..N\"?G~ g%#d;6*ŞŌK<|:[ҭ8ۂJTxQ8!A1`hS]1v,o?D!tqBWlcOD銦oGn}x#(ʿB*;Jzl *er"Sܴw?|Yɽ;!A1`hS]1@nM8}L{.<l+ %y#{O訽%ד_Uhi~&ɮ5 'NdHu'_CI6T *er*DHiu: \@BĎ,O~T0X;By(t ˫ׂoo>I QY|RD9=zeb6[_5>nEM *erj/92w>!amz {yܶçڬh9lr 5B6c[A!V?{[~a =<^ 0^ŗ^he/&/QG(\z˦G2|"u_MZcT EɄЅrJ]wp3%..DZ࢙S=-Y/1-c W\ݭT} z%ۂJ!t(!tBdngXxz"R]\qUc\jdLE"xԍ|!^I[P).%.[] /2digJ]\qеq㧹I*F%73~@Po2~# VB *ЅЅb KPʮ3#;5EtKB&AV.^_}7A$VUwY$xk[6_E6A}hy~m j+.R](J kؔ&~Fxt=@BĎߑؗ'f. ;%ς3|V[@k/Vb_R-Jv}EzHJю+7mr؃p1G[qBBQBrAoۗp/{Jnj%.A콓nO2f$CN)ȹK+$NɳQ`L1Hw g,X Ets]wOo8ۂJ!t(7袤iy m{UtgGa+$;+.A~4"uZp<~x{牴.L\;6o|8}Ѯ3PJ*wNFt_HU~㛜 ٵG 8  ian[[q:T\\/VT7n#U ]${y>J?Ʃ*s(p*Z-rQ5REL\;"""?/^X!}N ׿5== ֽ33>qN;eS֎? &S[jƽ!р@eҳG@»p0l̫wANƱkc'L J6s_qN!2.^;cRRBԧ|#`pAA!0?x Bl{Mqد?p ɩa^e̖FR]$Vu޼y  tadgSB_x7smN )ݽW[I'yyy/~w5U6:<*~778>~Tog@`JK+]*S K[~QB6l.%;N66AW(i|dj9h]J+L }ܡC&AvA@ɝ,}] ɩpe.ZVt7+ܥ+8'͜G_ML˘]Py0g* 9(}ˏXD稥yܭhz`h 8}Ȭ$ԅy684 &PsaU8X u!-t)/ ^TbXr,/ Xq_L!Pf1.2?X8 ]{N;#!Jk4b4:_Tt[R@CBoL-hx#)еtInfUdcC'%*w\ 0 k£oIIIvv6CUWw?iĈ{駟t.=I>85KJg1)iK\&h( Oou}ÑxFBLyʑuUr~ͽxO/bXe~sTXZW GekQ1pݺ} qIJBl_Ҏ?dCՠ0 kJZ <[io{KE8=1"R| ڻ?P=VqtJC]CnH[rw#4~pVcc#mY.SdcCҫbz:jmq+]k&UpIj k ]R &.f-^o"'M`97?[T 7-Z/[2sR0Dd98iOtQ%ZaٖmK)q 7?5b n7*é_\R%:I tadvMZx[`n[ KrߗtGK,<$T͇b+BY`Ga)o2{OU-$4.G%ramKS]2Y IPAq9t1XK0;t$.jK knr Yv`2cx9jT]?cR3;xv}%.BIVrwRrUҁ2#{Oa tZi]jR .V8EB3~yJJ+RZܡ\.L& kիV{8p-Kk':5XBΝ;]`- 3ZrB ٲh{O5N̜c%:}K') ᡫ\#Vw2q3]//\ [@ Gc'f9V{*Eٹ^3#.\^3Wb0yNn!(XA;8@gxUoO1;!9)ߠWoVJ ]S ˶ */%3&vrjh]{g$Sf= k'%Gwᆸj4N:ܥ~qT 2]ЅЅb=C5^`yБA!!a}z8)h(eyk.[1}K˘M=Aac^% %]'bfB!t) [wV}.CVь!kn;tJzXtT6@ E ŖAW,_e^p63;-$8eLTƜ-|s)..26tQ#Ra8dCX\*Q]kp%O-54/$݀kNy6+ KqN9ׂЅЅb˃O2$4 #|w sգliӳP 8++ <^w]ք<vM!tqB23-o 9T9y@\, 0E*BBG;ЅЅb˃KܪeqaQU9˶:d- E9()Rd mMXYyB".e g/PI{N#ϕ?m; E Ŗ'B5b'˛QQh/ۦ?J2j)[]\ХOuҕK>YWR8IHN+BB-O.'?G%5e}`ڿ xTt U,%mеE] ː xyLw2/#"WmkABQBBfI=F{iWEdM(FVfIw-..BRX,~+Mk* zYׂЅЅb$Х~Oʹ/C۶Dǵw KFtcR)[]\Х̀a#Tc JkABQBBeR?K2_VU2d ‘w-..Bb's|x^z-Ïu;E8 v]oA\.G&U>{0 20ti8mࡖB3~YC瞸ܸꗔ2E]N?$K+eOz/hvy' OњrF5[v\{iѹƉs:d8KVjHХ2 l4eI!\4>8|Ԩqo |@c_<3-/r GFuό٤)c ddjݴ.߫wx B!t: */%g@A ɩ 3foЋ+R+?8: t'sѲn⠮- {l9jZεz%w+;qjLX)[\KIB"3@JV]7.@1uСAΆsF7%Ii%I'7{@埽 !aN$ H%jЅrV]\eZ]:M8Cj}c,C[RQt=x ]5f. @\b-.# L+sBe#_O Z+F,IiisAP2kq4e3G)$]:n$..B2L]u۶w2-FuYncwj]N%%uisAWQѿ=}2uc-.# L+SA.[JSIiV, K0P:1BE]KX2!95ǁ5.4m.%|R>,!ax; ԧNvs e$!tqBie*i~z^T%{" t9V%j/tFJJې0> stream xXMFW`oU7 9@`f/yQ{&xW^wKCݽ4~2Dˇ88ǟ;a&`#ۧjH3rmL b/ Ebp!1H#ᮅDb"HsnDcHH(tp2]$g񾋄tI}$1ce9N@hv?Wp둘d Yr۠?菔H/c<ҔNzI]n?.ݗԆm1sR"U(W.2:k7u9{XǾ?`?/IФb_53A0QR_wo5#Fx{XA lhDU~/=`k}s+^e\rX0 [#ACc//չ1ZyҚ-= I8M+53mw&9$Ah"iA$ķ\&QOVEwްN'Mr9<[> stream xyTggι334; ʎ#"5 . `\B2Ѹ}T@QAv\&1qfofWޙ:oto=]aC􏈖pK8%pK%R-J[(n pK%R-J[(n pK%R-J[(n pK%R-J[(n pK%R-J[}N8lD]{6fG'<==GF>9vFb]JS2yzyKtѵX%C,kn%K$sss;xK[jV}4õ4>y9sMP-][.ygeHyqU||4))+:?g,EkSǀ˯?#w253ݝpZ]N[o/_ʟE^nBHhR2S Ԥ!#āTDѹVjEK%-׿/~_jȑ2%zw\kZf6O]EuiR@`|,pϏiѹM2\K[)an) G2wKvjkCeuSfӤpnYzK•GRyv^7\K[)RdZ5-㫯Yn~hN1Z[.^:+_O،OPqm溥K[)1i[ۺl Bdr,Ꮷn.}wD]k}~9WzzyTe{zz韸f.XB5k'xZNInIǭtK/)kw@)3Ōy̪&t㭌%1}܄z}jf-OuK7$Ç߷oߋ/q˓7ȻK:ziXQMsjqY.:~4cW|eټ n-/n [Aj¥+05n4OBf/1oXC0%-ӂ >PZݵG$ִYm%-`+wKJ)`[6>),[wnI)ifo?Zbf9UAwjV.n [[J+n)R3:^\ε9%e̶Sn1cil ͎Q1X%ln)S),voN"ڸѩo ^DӤ Sgr2u?[K[VG'^z?ScԵ>UqnI9t݃nB\ 6vP|w^UJؙgq#ݷ>ij-,n [[Zenas׳eR6ᖔe45(4LMm?I?.spKDD%T-kץ{O0ܤ[Os7}4pبnQ~~~=bG?o04ѵ%ls%=DP %R-%n-pKn ["pK([@)JDP %R-%n-pKn ["pK([@)JDP %R-%A[@-srrV[`=g8.pKpKpKpKpKpKpKpKpKpKpKpKpKpKpKpKpKpKpKpKpKpKpKpKpKpK+W?mu`6vMOh W.%8pKna-%b[Dn  ["v%8(pKna-%b[Dn  ["v 4ع.pKnal▶=8=6oTv\{˓fכn-;O)I.%["v3ni-]6pK&9^=b>!-kYaUS-/舁[lhxZnL[$pK ܒ,19}nYsa} Wnج-e4<-t-mn!-;~LJߘ2ݝ&Et^uw05 nֻ%[:S6 e0-9VyGT M}{V T O-)pKn鲁[rX얔# TaW9AaA_p C;EP͚{aHx'DZw  N9&`+7O3^wJ WVMH EII㫧elAk[+R O:pKilfݒ+wAaU01xͷӲ[N웢b ۞QFCQop3ᒺC<' o`Pmˁw@+ a.g/?iwH<"s-hd=uni|3c45ioeqU=bh%n8x=Ҍ?-}7>iϟoLQBb@-1PBbRfft"1z\di*ձ3ǡadLW> 6>Ҿ7`#j=Y>5G8cjn,T:ԌPTռk Ѷer yt7ce~)g - ܒ&ϡ:*r3niôIn)Tp qP,k<==IN5^`T~\͟v?Ènw;K//'{TȪrw&y{lxu_kuˊjE5s+N[ӻo^BP sa7Y8ޙ ='nxҁ[ hb;ꖋnJ$ # L%T0햍O^ھ4&2{+;RЧ_"'-1/y` E+뮓ѕ4dZGo O//v?6}|,'nMHwP']acƋ7*݃Z 1ium˼TQZ%i]מ?A{ Y>_||X~w-5mωtBۀC؎ce]sz{Y]"a.|Kg^|{CP2a.sBvļ%*r|AfdIoOҐz?:G+.9ΰ˩p%BjE/q0 Ђ-9T!oϘcnw-5mωtBۀC؎ Vrҝ>?;106"FnyN\ΐtw}*J^]4p\hw{ a踾:ccZ%OYskhӇQ1_Y nM,k{NnqD?iھӕF*}X?Ys%7 DMaD7#'F tpފ]-OV}a]^^l :p☵nasnaB|s۱s4>yFMI45Wm0$9҂l`RSK]z-E<#;O֬- pK;pK"-v-)E5пhѹf.XBSX3/0uKv3[O-nھQܰc[Uw?] )Jʮ=XY[hA8wn)D[ڹ8[R؈4a {Kλw*ih rkqY8|=ʚ6/-~^#SXcdu6rnasnaBuKsˆG/bu.R).mמ|MmCƧߗ~jn)9k7S|v0YkCn>*o%YRgp'zv.t|@ Ђ-9q-; R[:8[6I!,TBF! 3J0>z*Y_PuYYs;_SɸTڏUww2;/ ,k@_Oz' kfn/_p.J/-zg/)Vv+#Pɬ嬎Y[hA8wM2"H~\"2&p; RtqqDe-Aa]Eennn u\rju2q0r"w@2KZ(ų4ΖO %'^j[hA8wM+nய#D'TB$؎eطRcޚ\lr {cT;æo&o}!(W wMHbWMI4$,U mOk!|k,zQaq ^^ܭ{?yzhz39k /DYI(ӟ?Z;ֻgϬ~ᇿfbo깖5K05M٭*¶gwqFVHc8ح[:w,z{ﵶ:Ǖ[߼y_-seʕ+>zCY&hzXz۷Y|__'O>}LtUvþ~cb[ZQ)pKIJ=44m򗿔)عe׿5k*SU-O,L57SӺwثw+n<.;q }ڄ^k)svSѽH e 5w(> V/+7+7OFK:q}'Θ}ZF[ ]$(S2q/EMM[;_Ș>Mmo6C@`@`P9%TcqMhde_yy+&$hR2#5hֻ!*niXJ&vE%hSU--Lścg O%%>CiU6v}ww%l(-ZAkw6HxR^V6d}:A5ndRd*kE&j)^Ssf-.6T*dL̦bAuˊzE5sQ1+C"U &Q-ەL\{nn[%7؄$:/ZQr㕷rSnT8e|V=vꫵ#mH_܍Ãf/ДuX!"]{ZXs J@(kn}o^v(Ƴ{ :d6]tICKu$4/͢x#T-ULn)`T2M^ !7`0,^%7>yIH/ABHyC~T?`H.Lvеq!1YMh4\fҐz?%s" ײ1\CJ[$Է7UZnܸq68䖪w;'88F|8Pa^8=Su :{NgW{Ņfgg.D|߾}/^hmm>}5kexxx^^^KKUN`aa|/?R[ӲbA]-OV=}(Wa56[r!*ni(R `[4sKUR@-%R@&z~\ph; ^TXnFvДupa㓗:r?Jl-;_URBuwfRem̯ᠸ,ET6He_=22l̪HIrKSJ7FmF)P^[qKЖ#V.acs鶮ZqQ WnZh*,@b#m *o=լFRGT5uKJ)[\og_l-vK̵LkFqÎoU}Fإ@j7f5ܲHItvoJnR ,Цa!|!;'hQpa{WJ\xZ[Q_vKlᣨU5BRZ)/i,^-R@Y[r=$SICKPhot4T9K-j*2¾cgۏF=[D)--SJ Sg>,.ڌaڂ osR e-NPᾒ/ą꥛+ԟ].OY3vwKEX *uߚ-e*nYs[TaMmC8]{DRK6-TdEGNƱR1[2[]tvlƗ >?`HIJ݂nY=6 G9nxsR5ʺ5'(feF[uwQM35-RaҐNYs^O%S,8YwT)s\TG_5W̞߹[ۨfRzKW_{^S\{LZхJfec w q)Vf|vz7 "-ͺ7Aa4ݒmdؘ25n./YMEZz'St yˠч,ugݘwKڎM~05Mׄu]\nx);}omïWS*;y;ݚGn{oNFYthbU# Bw|jx_j~['j4cg":DJ_+Q}_ym9PLd9ߌ~gX󳸹(喻C)x3̊vlj] Ho*2ײϸE.tWf^\Yl[Z㲶cU&ɑ0NB Di:7'UPE+zDIzF>d ъߜԽWZp1;PSgՄåDsbT6&&}"3ʸʽI2chC']U:Ih,n.JO}T!&hYXa9u P@mì"-U5mƭzյG6)emG/J"c4}8luA vc,.KfdRMHG_[Zx`phR2KXv(> @zd~"hwǙܲ]-̚nQ0j9m0M5ݏFW-ќؤ# ouPߡcqsnɁs鵏n)Aʟ,Dl{vKHi7 etmv;k9JrnpF}:^튛٭N9*ioy^Vv޲}_p9'?䖥Ե>ȧn ukчb*?truKmDמ; Z%GA\ᖎrKNQ-Ե}E/OT]ݑ>>n9PFDm _hp?k8ұ@n i7^' 'LUۨ+lԌv'iaUqъT-$,\(I_Jf.X""_0̝͓'pKTu9W "&ћ"qp]5piL>j.߁ ((~Z%Gq-[:V-`;E q }yT+v8km?k8ұn 15ܲʆcI,C6HQj'?J)~m6U7pKG؎ R|'7j;t)lp_9W ͮ@OhɌseãQWӞʷ?6XߵGY 6'z:!a +n>:Zb-9p>Z.@nᖎ'vK./ $_ TvɮsFڄW}N,^S\{r.T2+{9[͓pK F kZ(6fT9MkXlE5,29}N[[-9l{n!&/߬nqyg9_ÓSڎMܒ" e^ϸEkxq~g˧CGWɌӸ%%-kM cW**J8Ct>XQ9M5k]r͉ހF돔7Xb-9lxO r3io.ҶfEm Ѷ| O:N.k;6q wMH^MYuOTl<^緦"á$$]{D 5?yzhR 89?gr6Of-bƼ5oQS窱uifj4ϯO_GT KMgtsO߰ń[r_r5w5H&LMx;8D6<8[X얈5q,D ܒC&z݇3ۗՉUOC[Eۦ8hxrbnMt-9iZ>)[?/֬fٸPdG8cmC=;}O Tηm[_ppK"m{t-mnɡv=ps܅z?:zy{=c뮲nC>TooMS #vgw !m% -&pK bV3InL[$pK ܒC&TŨ7&u XǮ;K:7(^=W$CB;E,/(@|8$k(m!a-RX8AÓҙI.%Mы ;Ȏݺll 2Er*47cVn>/}75#vFt0 IwP^BjĚ' tMt-9oa>=(}"nxO//n'/ S%[k'hxҁ[:S6 ePֵ>]Q}Kylv%w@孧nCۙz,3R%[k'hxҁ[:S6 eP8~vؘly+ֈCʢ[>Jjw$ms`뺹іpKt0͟-s-mdq~ ۵-m~Pp  8عQX["v%8k%b[p>p>pK8k%b[P-(naGD0pK["v%E_-; "Xm;Z{_иA-JJJJJJJJJJJJJJJJ{h7mv.O_mvGh[R6;e`; ;n KAL?"ZƳ?kӷc[u{MTh[Row;@>pKڠow;@>pKڠow;@>pKڠow;@>pKڠow;@>pKڠow;n#*fҲ\5U߂b%@mNܷc[fs˚[ [_-Wnج-j>pK ܒC[ kw1e;Mֽ3V3aj@?e0-9VyGT M}{V 8:t-9,vKʑOOODu[wyаO8VD"fͽox0$;'W['MW;qՆ[[yy+&$"cen%TXf=Ɲ䯥S*4pK ܒJ[˲ӕ\?L[n=|@i5G#/\4Py| 腩K䯅ٙ [}->W: ڳװ1?:X$%==GF>9vFb]tOIˌ/efTrK2pK+py=Ug~Hx'zx>7׉+2d nފ<($fJN]gɊO<,kwajflvRZXIҍn Pqh,kn%K|udts_EhHKI5Bri|s$>wZY[rXv7'>RzXGn{r5 WJZs|hDauXʯ?OIjJ?eka%J R8[.yg.[E|T5C$gZ㫧I6Vva9kᕛ韗w ?Zqt觿NWff|]b-.-9tSR|C!Mnš2weyd=[Oھc$OdUw?]}Ӯ[_K/ 6pKn#*&84tԗ$9R5?y45i<>q tPyQUwkąktKn qݒXefW'aC$~?,fO$õ+-)wk Tg|ux^2 os띒>ʧ̞OYK~3ChKfƝCBF4^aPw pKnNMd_*ߘ2hMܹ:CSzK]}\9Ah1\K2pKsݒCœfezDԶ~ocm_lzz.^Lv?y&}8=l|˹W3d߅^u@PCj~CKm]zcη7<8> nFjY[nݼðaᏧn.}w}DN~Sϗ/\x%EYL` jw-f--9uˀçef9ş it6>iAIcSf4ŒQ(]\23[dIQc [{{fe&$*B']U Ϟx,FִYg.ݡy ÿ& B;2Z `'-eanJ5ߧtɆ @ w \uפn$72קffp-,-9l93戁[n};d ҇G࢚fP_>r[;5\U4M7o"}rD6on=Cg+ .-9T]}8BC_'1/RYpK'ܹK$ofO^k.R2-oMeV][r%'pf_M YRzK•GRyv^7\KЦogn) G2qභ[ ȑL4Q[ ȑL%.}{ͽo~={ %z~cXYnxF:`H|?a3?CŵK?õmv-H&rK9&}wK9 o_u795S; +Z\O5\&31Zpy╞^^4EY_]ۘEq\jZZ6})L-&}sK Ʉ[RXٷ'^zK\} +$1oYuפn$72WOhvTuKCāni(hG4L%.5}]=4+7CwWc;eHXIda^q'.'KdvC|+Sn) odvnΝ[|d8wkܹ555j7SӺwثw+n<.;qJdL,}`ڄ0\A\^y)3wãYA!LǾ cefC})iwJUjj?b~ߦ:uG>Ú=ci^#6O^OJ)il0u]{D~>ZY[et j|o)(7w.F\{KA)͢xqp6 [~PmU6vR Qw;q˺DIy[?|0z/wx{lP-_D8퍏 ,!᝸[Yf6j%lS4oxoMRZsni7؄$z{/ZQrrSnT8e|V=ևGꫵ;p3}r7KQ@9nIm̊N ʮ= kRYO(kn}o^RPLټ7w "m.NYВj2K(sK(%aq߾)!6Tv{q9HT}m孧$Z#u?X4;Fb9PJw.R\-%R@&߁ޞ()Vu6f<{8cb3nSyjPB严05#K\| g.X".`>q0pW$[~Vn>/}WF[B)ߜD]ƝN-}İV-I&Cv6&}`GZ,X6};sK9JiET+Ps)W0hs?$o`qYnٙ :V%68yse)rK(%[xy{MSܿcJ%йZwwe q/V?]\{W~'μ=cISk` Lr*]EbyJsۃĹrٲe2R@&zC@ +z[8cIiT\'sW ݂쥻Nw"uK~A!\"uיSn7BrÆ PJ96ڻ%%-ܰ^Te-Aa\׽)_T_MV]z-.A]՝k}Ȏk+-W5*lfqn5 -84\<.}.B.h'/uBvZv)h >?ŲֳDD%l6ҽ'UdnR孧>8lT(?z?Q1#Fg7ZZr˒-U]\.qW#05 njDkܸ,A;K{\?t[vD+eQ䪅v²k$6ub%@mpJqz,*sW{G-9idԴCvN殢2ڮsT.\eν@i?}’ضGQg4kpK-s%;ij7EkskwXieY7 Sg>,.Z50mRzڄyӲ C;Qᾒ/ą +Zpgǽ}s]]{*R0pK-ܒE[V22pKśYN YuwQMsK.LNKICF:eͭs'QM͍ʹi.]ە}G_5W̞߹[vŐac(-sxD*{f]u@P0KI=t7NWfs5 6pKn"[vVr8pKś(+I7}y3˻IH[؝#wNT{ uY2S; Y\M-j>pKu+F3Ɓ[rD?z>PzF>d ъߜԽW>2&*/] _\l;$ ΟMKp?SnIsbT6&&C^7iV&}m pKڠow;@>pKڠow;mUt.&o`pHoם pK@?oε-9lD*>W[ܰUn Pع% h捻k[7Us S Év\{ ܒC&z݇WՉU-i{hh4{~hnɡM;uFZF%qp#S55Y6< T;Yci={q}'Θ}gp)BH TNCm .ZWn Pj7S 7]'~{ƜMJyrS}6}zPXN]s*}+!m% -&pK@?eP9U1ꍉnl2汛=ɤky T|LwssK_zm$٫y^^JkKTheeT}&,e,ɥfݝ_Wn P7цG/6ߧ_"ns6l[qًr̐1+7͛eji;ik#fR Iwn`}~hnɡx ČrӧQ-G.s34=>i7gg.H?frA)-j>pK ܒC&|lފ5+z({ tҍcυM2pK@?eP á&$>'ʊ[zzyq6>y)XI~Ph/6t-9ouhWTx&f]{{*oI[OݶvQn=XMz\)-j>pK ܒC&acƳ#XS~!WG8|?[&I%munn;O-j>pK ܒC&ZTN9u*Q5[}.%Ftڍ]tmG +M]_Pup\JsUww2;/ˋ S3Fm-ú[fŊ`,zGa'k2q0:pˁ:Hit=Ub  [,0hϿClBP^zޤYTBB']U3F- Ίm5[(n pK%R-J[(n pK%RQ{( endstream endobj 57 0 obj 21567 endobj 59 0 obj <> stream xZͫߟbfjI`ZtW8EWл_Ymq-r'-I9bO}y2'.po??0O?\508Mj"8ǫ>p5x57zp냮9㧆3:&1Mш(/ sZ8Y$<{IXQKHgi!P\ 9;ǐߖGmxNRMr1DD- ˈI:H_шB$|o쐈>bt6[7<{ pi"_3K9o>Shcѳb0Sg Pi㵎y+ mI7I)J76wpqͫMIȐ b썛Øg,: pPU,H\EX0gd]DUnUPpYn54WM{"kM4R >/ U5^`tErQrv*$n 5x=Avy !pz$;z$6qcW`&d`Yz.vH$<Дk@ZXuN/%u7<%V7-֎U97-RY~_{ {4zv/*@);e@7̥lE}ek5_bG0U|Oa; erdntM&jWΣ?"9~a,L< F2"p(eS2[249w=۬" dblYlH&MzB A{/d",9A5-$gmwS-*Df:XU-̶F1*R٨P^vv%w܇* uv vh;,==[K\*#0g/]t^V5)"$/YWRNf6,) MgR o*AN윉]mfa(̫;S-օ*hn]B +|y\ITW$g)v7Չ8Wij @axhs-r)}[=Z&,sWs g)U*jŏvX!A!?NqB٧cPO+8 endstream endobj 60 0 obj 1967 endobj 62 0 obj <> stream x[K;ϧyޭcCn 9ܲ!TJj۳ ݭG=~V]=o~sN_>?*SᲜaNR&>m!??0hYI**\UKoVogU[>'~>Ҹ?kU[kHT67N&Af+,a@ .§-H9+^U4<6iYf:'0Tf_]ȼ-M-0% uP^dU7_/bZl^ _Wi4~(MִpcK*e UrIuSGi m+\3s%6y7VsIv Ţ~jm6eb >\:@ԙxo.; &fOJjr7ԩuSXuX~ *tjWqxpųY &|^L +3Oiu`FWGJq endstream endobj 63 0 obj 2765 endobj 65 0 obj <> stream x[K# Wyw. 0 m@ANn`'@?DJTJϏZ(1Kt%.tO%)2N>W[\/i^Cy璅&&Bw4h'tt.~z|~N97Ū9-zӢ-Yu8Aꖏ.xE]VzeSj.*+~pQ)׳k[~? ݯqq*gnmL^3o@"R\E;Mu1apd/aaո%\؃-^g#NVR IX$6 B㥰R鍫׳#NzŗPU7Gi;kU;fGǰH\xhM],c6mR(mqgր=}Ti`ڇL]SKhcIʋڇ[屜B8 \8}6n@ML2,)D$T3)+OYZ~@j7DIgEUA3[+yt>{U쭒_4l\>M !=jdV5G;|Z__7N:;LD  FlYYY yEg!]mVsT<&e%,\5"DnÑU BMm)TBn֙Qb6ڨF,xK`x@p1tՉ^ÊT*S;볪9ݣb^0f a4M ;70j$ZJ AΡtln@5[lJHXDSjQ'*Qcl^`p{%OUb\w]t Hma7˴~WI*[ͻ,_鱡4ٶѶZ1CYn4gFtXrQT` 8MS%95'00676m/eo#$:+jR.SGDC(hKHKv1MQwuO,TbnNѽab^m{ LT)z:rTe'ou:G1Շ1 ˺>5 XbwJ**HiKgXi<塆()~#[7\NTCHPTdW_VHy;H];Q#Եy_q{㒅3Q&MGҊ>4Ҽ$GU9sQ ms75֐V#ΐ}j^iZckMsbSFY˸y&4zTub_{=De/3VҸJq##cV[:9A^(Z; {0%}zELfĮ^z`c6% xeV> IT/KqпON?.yy endstream endobj 66 0 obj 3255 endobj 68 0 obj <> stream xYK6ޟ_u }]fBH6SU*I%rnhpw[V_=nz&ofNǗwL]-)´On4; O-M`7y͹kËRk69{oG4m8!yv?V=i3}]ie+򋹫@g\]%/WWKxE~^hm ѤC,*fefwI?(ߋcˆh! 0)bC;L'Zxp'˲\#~|m+|?$ ޼`X5d+>=H)ޗa?ac'cA&le~nR%Ѩgb-ՒiM%:?"t!z.Uз6;ʪe?*k2c"2 Zh<6B ԏ", ʼy˫*o`㲉~phCʤVIIBy;VQG(2{#sw%WEYGg9h?^ dE O8ɩjMŶ(=Rbfzϑb 'GR?+WX޷h?o;pe(Vr."'b(>.U]1B +70|. <>6w 6W~8dgM;-BU%AJPHLnho03T$\lG74m0"9L z\EЮ%e#xL*Jچ*SvV% b3\?4V3`|!лHI ^>: 0{ gAAQʑ-L22kCǿI%T|@3H%:Dp@ \[$& Uت))43?P1 y(E7#JԉdT3٫G:[2DuD(bTj0q+Gbjlv=r:pC:@E6+6#?(OsDλؐ#p!i4;*enNwk=Q lch,(L"Cbu\j7kQǫ|wT0s؁mTߧ q Tm. ި ?c)=/rf_s:Ud79\/;JЖ#jlȁKѶ5ƇՃ/~Ler0lķ)mH~ ka?jf > stream xZI+7ϯ9`G* 0ۇ9ܲ@䒿J*6vx<R-_js>))=?/zV =CJ=X8|X=g 8_S3_L=\l/l~Z/|v|qv3V@xh(,%W'xh+Q[B\>E"0D"_/G8;(M)VX#QH+Qlgw Z(=AoS1Ȯ41+]!VpXS0rLp&bo\{fȂ` Nؘc*`кdW[OCv/ۂ$m|׋H*T8B7ȡ3OjeYh<3^GCƯU!V}ͺ$ LO̤ sy^a* CPU7TBGZҨA l=\<KLw8b7E1Vӓ9Äu@+DG{DԊP` EThؠ 5a*yD׻ḳ܍kC)Tq(-B_%fyGBK9M~LeἭ6-M`xdFe<;%2Ʈ\iࡒNvmRӤxSSSn'.PqU]1G7oCAiR1B8l?T`Q)IJ932+3W&k`p;-RPI_JcNݯ#CaK^1}GЪ nQ: ;3~^V΢1x̬bk lC\&P"v&.X?}k +]kE.VTzYXl<lDc*n9Q]#E$GXke#Bi.|)D!`{pg[V=8/VM%.z+s'߳Rd )QEe#e OsdO`*[M?X5hIZRM`f]8WGZ]5(S:BD[L cwqa驲3f{/zTL* $1T#*Qy&Gw1icJUd T\I6=t} ܕɮfG FShGP kYX÷US]kU`e˒uz$=Njݔ>4 U"eJȜ6X!EZbU ˒ݤEt'V%`\Z?i zzҫ+ۯۃϋh¬f{#՝`+0< _lxmܞO:Ue>qƋѸ3 )M>)l)al-o,ωcsb cAKVS4x1qxA2F3T`GJHUJ!5l /lm/vU->95:)%DZ⎪7;(,uASQY4&?iW&snGϖs1UIDʒcOlQ.>4N#O"po(rW &0o,ZTXE)W&rbQTZ f+Y^enC{5h6Ey8r0Nů6QSy0b>5uZЧ܋ 2ycLnqphJxP݌vi<9ߨiv0.-jp4a|n_Wu'A%1cԺΗiQL[vYw^ JJ_?>2i'Lh%@mfy?w kMMk0(= yKE)$ [2Կ _$?W G/ *bɫB޵NSEQj;KuECUٔJ>6g c^kMeƭUk _džI]ːUih;1AG#)XEq=)8;O0oe:D/lPo\w3Zզ~5UՆnբt&tuCKuL]Rfs,1{Diௌvvk zחj”8 혌:>f\MnKU\ 5/yj`2CA\v'_pg+ܡjK S-H{3Nel@C{0(^BM Cny0{)e SPA2Q!Tm+#p|tN+ endstream endobj 72 0 obj 2432 endobj 74 0 obj <> stream x[Kĸϯs;zۆ03}ma [6K~TJe2tzUoo2{/?]oz~se??ź?v7ָjƛ`&|t7KkuQ>?Z_6[{oqgkʚeS$"i6" mL>&=Kb|K8K̫ۯfb\~ ҏy,_mv~( X [=,#LؒmԲbV2YtaZqyaPHB"R Y ecax*dG7uvLSSߨwgLȥ$W `@cѳ2·_e«Q+ڙh'p}BI\Yh̻g@7s{ٕ'ܚ"P+t>&ݶv/x0&tD&@1{!~頂p3,G$K, +{*q@ @쩍E aˑv2؏Aҽ?yHh—;ё!z 1M|♘W Y șN~^gv'$'џM8g]4IU ̫a@>+ ~ eA9@A7(s,lNIw.PZIf~*JaR|4=Е Yd EJa!&1A8^(ĀOiNub6T(VC1>s `m6wvRLxƣ‹UShGC ֱXtNR8YPnI$pu]4127u &M(Ď=YϬ,VD 2v `Ʒ4%( ŒrH;BKQt8BB}ln7($%$E.xҫf n l[qTaᚠUj5J~&+F$'W̓ީyi@K{Cj߱wjpfSɦ,ٸ4ސi\yUGp1@R?*vqOD#Zy6uoj-ZAfJ\-Zݕ.􃜛hkBY|D8n'ϑF)s%G!n g-H-0hqwpN&Vw+qlXxl) 4 IQ*iJƅ*]X\ؖUUwQPo ]-HkIEx/%d&q~ Bد2B!Ig`'z,% 3 GYr 8!WX~R<=ak)8uM+ :2`Ri1O˜Pj.ZlAz.+=T/ڔ6Y\W Qb]7J]S zt vIhSLH ɨv[ɳ5IAEÄW>Wg1| ɉJr8/tzFl2 .d(U"Xa.v!CF%[@FI2 IO qƋmbN]*8r%u7ơO(и[3v= Ţ u4"*$سXa}r~豂:??걪*AFK\Kkg"VL7KZ: [*&T>FhXG=*R= KAeܼOY}lKK7Gd9]XEL#jh 啤GOǡ#"{_E?tdԠpB4־WJ=gVuڗιK {3ԁת}Β"0Rt{qSkH0ڔꞩ9k mq3R}^h#W-=9*Jbƶ E%=IfUZ;oYh >LŊ=S.t( ^+;dԤ4{WEYv`e*:piaY΢GS_DϢ\:Jwti<1XFT% oE.[Oi'58׮W tȧaiHG sPt]C7FNB aKѯh`]:RlLPMzxjܨ14?ʋX6]HXo12XཚIyb; i,GiǴ&sd^'́lw]:)1H}j 4BT`ut͐SWSYqpa%1PuN׬('~chYnr0rѵrs\MMCP7l~:̃(=)}79*y|L7lY/X'(dCeC ~$*cI6> stream xK4> 3*4 %9,e7%?,r=w@MWzzUM%}D(ӯ?ocqi1O9e ~~A ~$YK#k…V|q`T ubrziO_?xrN.ٟ\ۙ~s>=\Nn|J>۷iK{D,:aN $df!Fq|>€D104A(Bȋgk.pDCv; {?^vXΡs*3/;ᄛ4~L~~JiGn5BV0~0,2u4S=GC$sfIf/ Yރz\LGHp:(ʉ碠/&V$("Y;^bBbK(4'e檴.:w\)9i f#WkxZB짉>x PGxK3)뜙raa%OjjsG[>ް {@$ܦss#QN!bG y? ʨe%98,n#IܱW;I`$/oj4U+/(Tؖ#a=~FKU9AQnVY׮{/LU}aŋ&,z5vr׮!Ifѝ5D I*e##/#L9QMFȨ!wOfwt FLNLk _w2ii* ψ`0f0tǛO>{TsG> 3U8U E[+95L6rjA\8? G|?R5D<<mn81aDekO& ZTRHY(B䇺`]V*NJ(p顼C)ZbuZd8Zs5VfvA/ɍኆ؞)% _b'SeUvYU6Z0ȤAԶŕWzHVq½a]fnKCJ(q傳Fc4ZD+.ڇh]8Y^Y !x0mVWmKVʶ)ߣ2>lx\Zա;e[ }P5Wq׏ҟtC]U}ZSnHTnmί[XR8sZ&F*  vs M,[vĈ %\OÆY.m7{StY6HKȏFԗk\% z> :mJ>smSUg$[s1dSW6}vX點b#[iE&P?V<2GyLFk1`y۾ic6enf p:-;o3fb-xF=O;G=I߳/l`N~l/s,u{È{sTaSivpRG@kQ%=˟:=I +]t0> ػ}7n,߳66̓o!B=76g k@7X@5eKŬU@C*]l*`6`y -@;6P J0i6D1V<ke}^[iVyyQgQCnA1eeQhꇟ5W,*^߽RWniK5gi瀊Уoc%_gf2r'J u^6B‰tEGvΔl xDkxjEmSUmxQ$3O,.ؼס|ߺK74Tj|ay޼T)%ׁXfO⧶> stream x[+Ouc5$Y%3 d6QUIZjLTׯJ:ï_=JO8}ZNk8xЋOnѧ)hҬ{VltϩhN\Bz%n߾'w>hs_J+s9곲)1gk%ՊzY<߾~H[O~@[bQeV^\Y;AgDHl@ķ1a6Cep ES,9gmmU5n]AރF^eU8>.+LmbϠDzMߓtNʠ g^lw*&idIBVَ hUc :긨QT:E;\ժ#YSHķX&}WkZ. h^$@)ltAK- yqB^bnK`$pKf[@wu]E^+ AÂ3':.ܯZLEHF"Bcd1opyFU9⭾*-ņ[g,D,D';s,44_Z )1&ތFdiqafU)Y3f%%!3) ,ً% 76­g3Q {)Ō3qMl^D?FoSjB19옢US4[m+LCke|Ѧ1:H8%&KdVgR>/f362T?Z3 H"SF YVgϚ@삒ة\R}|67ovrʓBIKPzLTQ`~[Xbрp K U1z/+x& 6˚ɜ ;%bJ\,Z {D/!>[\^\m R /Ydڷ|u Cjd˵*w6 dD&Ֆ} I`#<8^驤c8Q3b#(OQ})gRs` "j#/<]D5KIO;_'\>|+gQZuq ȷ9ʭ"iW"5Lkɓ/Rk.slfJ`^/n H$&’ Mi1kIDEIؖP䔐%"^=9BD j?R8)hɃ~(CR:mMltjo4;JU=TPq Z R_*azE4euNjjO'2Ln*Bɽ2`]+(r׻h`y7j̢:칢MWsM9Bsov.|:~fi(βePf`GvU5X͙V-G-#NO ,6w8 gZ\pe)һ 0b/+ݵfk61L= נeXtC!dxh5p.f9bB'd  3fgfVnpsG]S:%LaL32:)䌹s|NAg]7GGW2o:zĂD8D5dca^Wl3kG{׏OT=Xې[]¦SnX <!4l;a¸Vyì!zl*u j+h#Kvo`3]9e E7DRpU;'uƺ.~{ZPxfGpĝ0V ]ukrwtDJUZsrwg]8]S37qlhXk"F1wac6epaKxS'kx|k)n . ҿ6 _pq?̲r^W_:qV{Z[ZÒitfsN. E.L bLD,b"G&4K9‚|/0/ 7/1rt6f; h2m|TPNQ&m@t0)abIG!LȬTLjNbSDBd&9#U*2MVn>IQx>qɌ1vx>lk#qv|TepܶgMOanRSwE0 S'bO=w~&/> stream x[I# _`Vh !@撿$6i4.IIҘ3ߓ9pO_N0O˿#s:%qpN8_Xpud m0Č;cƴ8P9۫|r7&|39˥i4Cݿ}XOJퟧp{/~8Mڋ3|3p s拹>N^|ǯ 2Mޔ>E#`^d&olFqlܣD$ R!/\|3쒛&9 W=i.F_QV jE^4 SeYK;Ms!( nb: W'OYt"'"fWY9 Ђ\hK4Y(IХ-d=F]9ce,L27 nͷoIбc+k&\@Khw3٧y@4{vRJfF~!zw7f2M(87Sѣ<}yǃ=S!K`r[6ٕHW2/Tx3{D)ld9 A>/ywJ᧯ }1,|x3} pi#r=Ry?؟vީ}eC@%0Yzv'< /;$"L+ dh"qdQhF>i l=n(xA/Q,P?z} zH? {4Zoc Z/Zت}V2ZVC575oQ?ݿ8QW~}S@ZvH}t+Kc+Coᵯ+iiU0[4"ZjuG ]\ø7 47 )r1݅Ɖ:3w4u:CP?Բ.i'EY†YJ&/oX !5;?$d R^Y4%Y4v=kOXtr2UjDY!]@̏:hêh3'*@,yEY~TӹWoe/rZhwXpUk@?ifa5w:6YRXRVƭzy+9xv\Q/ja;*h(P$q<#j#1z0LƦΪ {TZN(g{+_V `Fsįܫꩻ٣e'Xr+d0=m 7R{$Mޯ-X6,O"yTo9/E]Rp]J#]kaܲ!Ŵ5TZ6*NTֆm9ZK%R,"jxysmT 6V}LZo76?g6OWy{ Fn/[џiGt?tԽ_^:D~Ԋ[*,-Tk$&3p.ڪKÆ13δ}ffgTm|l~@`L/du nZRQIm)̵K6-AcljDFXGg)o?kvx+%jX SŶsi6H@Nuy(:=6J7uS/QI ],״@nf~ph&1͒1n1e(skT{w.0m䚔l~MNnyw jKDBԁ?N w=M8nj3h*p3E5;̰I]@}DNN HzVp-֞Yd́z I>5.}6B0'i8ȝr[p ù2iڟD"o2}ҤNHO:4dWmIKv`,]_MNģq-D`ꉔc1UC P/pч/qs~LlvŻUx(l{X8QePD?Z+|l' nC6:X% ;W%n^R mw2$Y;k0*֩Rvux꒵RUTRղykQ5h䣮△s8tUAdsq6<*RxhCʦ:[ֺ_?[U{™}ѓۺ_Oz endstream endobj 84 0 obj 2987 endobj 86 0 obj <> stream x[K# ϯyz 0 m!!-r(Pu %"?yb.߅%{xϗ?~_.fދ/ۿ[3;~_xa]ȳoy4ĸ&7)N5 N5 L5 .5Q&O5O53>#M )^0W7f&Do-^M~W _u33=Lm-IU4LAnM PrlI@[Ofbȷ7{vNYzkJ!{åh' -HZ]/[f*x]OY/$E+,Мj=ۜ.X+a%x+c{`^ՃgjsGrԈ781"G8a.ts t4|*tPA󀗱p7M4/}$l)NؤXSpB:!اhf$̂C(׃o"[A";:_kUG>t:qqi3z8o6/ী`=uuvmܾn >l1ѫ5/4xB=)emr5yQ6ɈlC̚;sFH&mn}@ʹȶ6!.Dx 5y_ۄlv5[6=yxrnÐp~3m\M <{sc }a-`kݹ` +?!5G0dah1 sU F?!_OcS~!4ד%3-nC+3L.S{\)aht!MsH&B'>p>$x&~WSt+EncUjzV T\N"3ZX DVYXTqءbq PթwR Z<+I}CK;?[DRBUQ`a؛y;0jU;"jA_4- nGzx/XHDg_&:Ѷ^BC:k; \! uT"-Ŋ[?,m#5_+\aיjA#HpRXuzt5J hR`9—ִ5Uґ]wUs:6e[ ^a*B]Ev{C–BS|;'ty g]nP1#An6oh~FrɊ=m+br:⦻Cs+V_1İ~dwv8Gȕ/M =lvE *tY udQ>h@Bcu]2\{[4Td|gu,;@W牚,&$#gB>+Grn|_}p 7leȧcX+iCۄ*/b(@+}7ch?R*ۏLf?MѶnsqxPQB%ޙR)SwEeƭ=&t_lak;q:ƛɞ_Uڶo>PlPUm/Fy"E=D]HhxĜqcdTiV7*^S<^Duά^PR^;<[~pY{My^6)=j ?22 4E&F2JRnyQ 톄:]WI&(D~bCF vX0H8vh_5tGG("J5a.if,6HKR9nwI~sPZ^/h|qQcivpDcZG"uOsȪqx  7tis:`5h0\i`îc\6 iL@ŗ0g.8mw8+{2zrq>CIW&#Q|^v5WS'֕ &W=wCGêW5q5߫Vc[lwNޗ,PE6ꈷpUImv99bN1p^W'C24(ُW[v u|zt _ͭV aƭn͓£aZx Ղ/3{$ۘ[7$B@:XIn`L$>R[KYǀxuTʦt|ԍ>y}{iLD7=jvjrd>v*UﯩM!K0VZ-lwiqUs6twEi ʢǭ;uOib̹0ggNKlQzbyŜ{Amzv`.Ij\RkbPwBkݫ _qQ8^1)=+{i`5d{\ZNE&M^wO-S9vOɉrdWn23J w[brFV<@jz1zs`p=Gվhצ2~ؒߏNlRMQ6 =JMNS4FesY?}z7kXj{L_] opRT$oc)NNZ.dYWN:=LX N;>?w ?HaX$űZWnsmV6=pmMݱ^!I ?q.;0Dǃ<b;6z-;൹ pba*y/L^O?|1b8}RW^uIpV&[rAph? B-GRSY4I:bBa/9 a"4){ EowB20kZwE﬎; (Ov8Mdtv2Eu78๧r_{ɦR~*ܷY~t3l@^ pݩzyd&ܧÁgoGQoj,8~?T紃o>f l۶)fo2)}WOJdrѿZBԕhvNFDt~%=ɏ˼6IO25[ڢuz)ٴ :ۻ|@' V:BgvuD!쎡kk<.O+4Y7:6Ew3qg& XbVY] endstream endobj 87 0 obj 3546 endobj 89 0 obj <> stream xYˊ,Wzˊ#Ps@; xal>zfViJ)^GG9߷Lf2)::7sX -a7,"M(`9#<7{3y ۱/d% }q }q}qь>b`06`[ {(q2^>ޘ$yv?~ѐ;5MП|p4w4S<>M?/2~_onL[fVjT>ɮ/#a^ΘpP;tmJjOuU*TD2T"fSK5NAA\Dlu" *uRQYk`ӣ;eCEY"6^+G(P䶢HWmIȗj{nz3]&\aIsQ̮.sl&usn[_z/bTQ&n 9l/vwgoΞ{_jQ?6 _CfIko6ҔZNS) Zkw!Hp C MZ?K)/cE.ۿzh$϶~4l-$X+/KMZwXw@Ls?)l@a:6s#T_[ v#_E!޴od{#F˪g«扼"?<$RO{Pu(҆eH;Bmh:(%R/E]ns)cSɮHn^)ndT]-9Y-w  ݉9PtQ?_ɗMNٶU)w:D WGBOWɝ "zxҡ%r}dw~rV%X˅:#f2AW! {I֞@S]x~j堔qFI+lDo/Zz ;8֎\ykk[L3pBT~ endstream endobj 90 0 obj 2042 endobj 92 0 obj <> stream xZM3 9>GaX&z(ҿ_RDjf4&^{$Q?RcpNd[lOr~:͜O?~{ N>s<O<l$tE2fr83yʉӑN9ɘ3g7q(1X9RRԊ7 MɟyN`O_b;\3ބX~&\;\ܮ|Y>?h=~^mLsm,h'kW;<$4Mxg]3XZ_Yhw [69uAgwxwX5pqqXnY` F} emBCӑ7Da,%]>6JІ& 9.( b^|-eq585OJߋHBB>U7y0gee+u=Q߅sJ47}j:Ie SV-m!1=C =yYe0TQ9:82~he Ŧ~H[z٥Qq1WX{RBP{Z{B$6g@d'>ҴS#MhAQa$0M{Y˂Cv utVĀlA,9]JeԂcLvinLeAQȞV#1K=Hӈx;༕@0GxlNSlDŽC0 *e2J0 6}->(6&8i*{MWp=n3\n?ABQLK˨Cf;}P\^tJF7DwVVTѨz$DgBl5wR6ƪFT[t;S*/v+J|id Z!0+kmל1iCq5:ɍ≁UW"U4cV'OĻ dm3\?{϶g31_>ȥ_Sb>a,L27n򾽀M&3nl4ɸ^@Mnѱh t!cPc M@9fΙxDyBQ66EB>PH~[\yG=mF:]V=jbAa>龅=x 5y"+@D9/9pӳx YO{o2& /9LI\;N7)@IΛj]voeuAV0w(r2GJ`POm߈[͂pz%i<~)AS/^d>nJ^f VUۭ_[yݝo2svN^_R Hvm,zWA6*qzű]2s,0CKRg(ӳhm> stream xZK Um`|mrr9ؽ)RLIwӟoѿ/x\er0?~{2K8. >r'(@:F~6z$ԗb_Ʊ+IBW+ICWI NȈ7EN>m^8pugqNW7q>ǫG~=}wwX#f)?HD,sB0.1I &q}L= K"Ț f E6o&fuPwxw4TQ "2?ʗ\r V:OX1CLH2Wv 6a"y5T3|F>`͗@EUר{u&MW,(&=o I1=/mev`@ SہUFLVޔOdw_ D0q!]1{m]*A:M~Z/&)=]Ź,E1*jQ%iyhޓ J^4A!:< LTB 8.x#[s7=,.*'dPp 8{]Rզ<ҩ+ִes&|ړ_X)rԠ$I1F7-9!R6vh`_88 ?߄@N~^ױƲjvKN㌭z[U Vu޴*0j/:J" 4*0wsۡxh!k(DazqYpT4uCYj1C5_cmH Pf EJ}˃`Jr7i-6FSwF>8Y&zΨP!A>#HThwbLpϪOeQI(S/_r]1Y!TKYjG~.7F8W "][WpgimDc;s:p3=eؼ-|km9llz.6MjC+sKpWKmL۽派NmE[O:,ǎC G`CuU@dx[io $a#of~L+po=| :ʻG0BHt˛3Fmm.8յ˿Wb^vn1.{nјAќ=yG%m|Q6%m,&*vpe"e^b25ڌ~׌Ҽijuom"n9mcb}aϮ;RXj+!g~uf1f'vL&$ڦ-ԗB7f䶆[W+eںs,jps-O)Y~q94?oy endstream endobj 96 0 obj 2199 endobj 98 0 obj <> stream xXK6y;U=>0C-K~J%{,lzW}U2pFh6߾\~4u[~[w|ڎxo|~{_gό#AΞmW Kgҙa,0M4<...o?-8 o_MW n;xuv1wuEsy'ڀDSʃUʂ4;Fˇ{B<9{3XqCb1NW㓩ɯt*.SpwXm޶F'!](Me,\OnLQ'!1΁x) #n[BȯlPjr7>K!5UX^Kiݤ5 zNB9Ĕ@'f870^ sMr.@1o(+ڰ0 p!c9 b_)$Q)hf_' P"ѹyG P%nFSUg: ^SӵZ3S٤ Zdz4O'.xvaCo]91a61Ke_`#Q1k̈Tڀfr tx|q\}683|֎8&?i6lyb G j"4gJIækd #ieٞ:W ֓\{H5 #in؉ƨeSɯX֙*w(rPJYk~LlT!DZk I$ʒyx[ Yf7YV v/1- H8e;J4 mNƪz*{Af$Q:Y Sǥ4乌K穰NRJ`.| %t͎z790^ KN֡'ʨFKXWԏ+aת㬖34q"iZof^*% JӷkBٴ-k0X6Mń2w?{5IJkYI,5$#>}JۭNhǣyuaCSC98^/YQ6u \gۭ#LíŒfY~N;EP-["gNmq> stream x[K+Wx=`GoU1]&4df70 lG!Hr\.wY::r<m M<>YYv$ϋ *>8ķ☰E `k l?ⒾMJVHZ^@ g,~57 6,a@|^epqyWe33E%q:vpB^iX8!sN2$ wJ IC]ե0lt}YF8 l~Rw$|(^;,e5R b4K8nP WȢOb0KځI`7j3挒h1vEN=i7t$PBZZ C7"`!" 2"/~MaR`񫤤 Hl;5$r`a' ]ژfIf E-DK(䈇.^RV %O'0U*s(d )yr1ꏊ Ͳc792IK CT 7TX5 '[kjHKW>J^(W&(X^>I- -V䀝ڈGX֖pf[j* G5YZRJ<pz' ֵ_AD:EGp sΑ ͟k f %o.s@>8 %>R&Eve"HA4Wc)vM\g^Q%5:b@#y3_N9QLZxSv6#|(9i> J|(`vpW DdJ).ٹOu 33]TA-JFbFx싘U^Z|D քCUd̀5ݚ 8*2sl6kzHEЩPoe1j / (PmO?wj6$_B߉|(͇~B'*1sf@LDH6{8C0aaD̘\^_jx|W +H(we!#E놗Z҂MaOu8Ճ 0ebNiFqs4J2 kj ᅉ K'6!=u:2GHb+_EvT[ۦ,+9kL!y6# ADgqB(j[~tQBa%mR4Pn:;c' :s5ROcDshϟ6pv]D(M 937rA|N,'|c oDLyv)i.Ah>Z8Bhp#Ȱ  }'V͙$`)؋9sL py2A!A R#UKjMl'F'Z yGM&]ԆEcWR]CUAfA:OJR6VĚW#6AwF!A"Q(A+Iao~9/_nݰ;5@TǼURxށV ! nؽk~OĄRGy2Aa‰6,a5WoĽYraF }!8 PbÞ6CeTJ 6Ǘfw6 v8 CdJ_Kr!hz.l&\Ŵr/'uKuP0/(ҔeS+n65g4C0׺B:0q*XvCϿ:K/=vo*MLn0af& A]fݸsPi^:hUC{_6j*^ ^Z]D 髕 CP< ya)߉KC'`O@;-X6N\HZ^ #? `8n56^Y)WZDWYurBk RŪf {i2EThFR%-[nXi֬f[Sjh:G bl+3K%A}}> stream xZˎ+ ߯:;RUv^d7@Ye M~?zP䑪Tv}m")>I{߯Ŀur_?uE~4,fi:]&^<:$(!S+)$>BGfg`0k}a*r4X3z! d!K3Tu7,z,0H:ԯWbV, #C$A ޙg!^UUI肽إ!w&.tuӉ;͛-Q욾}>viŹ݇a)lMlt*ڤʢk%k+Z@r|zBKy,3 PvpKͺgnq{F˛ϳ]JշdGCpP᫹Zȅj[ C҈[ֵC;s : z?4A҄zYSfޮIYjm2DYQO¡Rl(SyPb\a <"rwQ҈is[r E{=Yܫ h GspWɇ&R?LPa[B @CHgv.]P1mXvj4 8H~ʞ>W~>N~6qyݍ^po&͇$ V{)r/ڜu.?.蒱^3@|pxj.f+ģ 2Mo!+j(cB/G' -]"jzgpWcc^3ǫ|^сd;X@ c%ԡULJ"cݙ4W'~ wgeTcIU)fx;j)\GB11d`r= fǭ*BD<8:K1`rFhɃT82iuR 0Uia7cQMw5^A*|vUB MǒN>r;K @keM?'VAq Q̛3ofJM( q"!,%FC ,eE ;C kPI;M̛ ,&} aZ>: fz5jB>kͧ@$r[Sĝ'~!tTTPXa{)gnyQFBK 4A)Z+-v% |HWXk)+;EWMwg'ʁ%Ƣj?p3¼n/K,-boPJ0d e MOYS@'yRHK'!mX݀VPl*Qkv@FK'8^E;J_\K}Ιqgx? <&k-K7գڮ\~!441+XEEc9sJ'BJZ sQHɈD$%|K9e h&eSIa \5/\-RҼwmAzOQ[tSK|EC(Zƙ_ZS]횜;= EM!T*z^;ԲA8E3rpK J(2 4Qd+8P% /;TЌBʷ4|K3D &u`Į)nm}6B%#|)5Bfb]@M%l9?V^zu }E9J%tVfi8V_6~ .V. E3&`4[^=uisފ鈨0o*S7`J_>@=6"uv]!ñmck<߭*GTSA<6r ifu zձ먏Cd29#*|YO?&S:;!kC_y7Oٶh܀ʶ7Z,? vg|Z&sf$DqT>qyF`ѣ<h:] [>7]4{hI>*ǵ1H ˊNl endstream endobj 105 0 obj 2747 endobj 107 0 obj <> stream x[ˎ+Wh@ 3#@vN"$6`,,IvtG|yy'q/ڋ:y#/]o_oJʋ9-9}ק}}b;[+׌G Q$RRUMG8Qnur,a܄ /"&9%- fuhq r$9/y'Fx TxgOCVxk#Dz$$\iiQY`Q./.xz?8%@$ Xix!V2O!!VJ}bNzG%]X3aH-c<ꅑy&Ob׊iE@Ge?i[$$2)ۦCy =\ZYFp\Tuǘ'"Y TУ܆b1r #Y@Q 6)T!!@X#niygƑye0{MM0.ttke2fRY3{6WMn-n+0^iDl1* #r19$z(FQ`HLb!y,b29'O**w>8I,\VVzpBVCG5c:2F$Ef9F(<_K73cmcQVU{Jog:0; @nJq 7 KS F6,yJa j!p#ԷJ+@k$;T1& ,C4y8; H1z;NkA 4,>צj8nq@xu.`\!O 5(#*+k\[R=$Ӑ:^|'zOĂuigrh̬\>Pٖcig9X}GvkQZ$W *һҗh`>z\W6jۉ]uZ%[+R2ШTX'Ŭx#KD)zL)ҬC|FdE1uZm9Fg\2gnQ ل+10 uOڐTތk@S;Z&oY ,, 8\(Iy4T4ׄjYΏVt gv@Q~N5Hխ1 {oA- uy(!{"٩m615/<0F06<'Fmn0+7m"q2m՝hw9C>-]cp9b{y%ɳB ;%^/e(.ׁ4j1g5GFXm)JYp¦VUvR1#M**V0Ah)X!WA|2\Ec/LEZ+6~xA ".]eGMiIAzY_ŝ~,O4:=LҗMQ&TBC~IB*19?Ih2=|0e&:,c2]AJ-膸L9o0^)z+ dv'"T!#OB`3tQ92n 58@ DD(r)({KeQ.%.Ȟ͌4f IUuޢ6?H{ddᬁam攙 ;mg]fvh;Y9JJ.-:lʝ,YU^fa"b -_6vB-|xZkM8ʎ2#İ);^ٱ R\Z.=JrȒU@lRѹa7İcps1yN[)k ^t;kg5h}(B׹ cO,֭\XT&ۄ]Z?Km-veB)-$ $F҇괕(Hm8h2ڊN-$w4|.7Ha䶅h.TD&\P%kRtDݓ^fR:mʚ XkqaK>)vg_ A)pij2  =–;ɻ@E\"RsUMO8 7sR*\ŁUخX|~i|adsu-67U-\qo{ 55O߻r"dӷW[x(?:Bbuu[ *8`Mn~Mw~f,dr]:p;Ѐu~IFjD[@K_M{[^WwZrM9[)kL0UE1G@rޛa2oYFZnN]Y"EL ܝw4zzōR)S T<}g^k6".yOj!Ԏ endstream endobj 108 0 obj 2990 endobj 110 0 obj <> stream xYM6ȹ|B vCoJo Od;%lb[̛7oFJ|W6ڳs8ߜxs$7kٜLs<1oX_11flkD} &xqDbLl"1HDH(6Fm!M$BZD[#H𠉄Dk"VbEۊn+VXmŲo+][lۊeQl.~PǛp9}|v/$w!{ |e |oBkP߻4oƵ="fv j/lVlr8kY0` cr`?@׶L {Ps-v\bp&(]JJɜFZe"oׄ:/k3Cӣlywpdfd!N Xh^AtccaIL_`Nu,HuY ŇIIY(Ow|(d!{CXhF \d+W'&VtCt*2Qx\~?ffVMsĕ]%%; }m u(CxiEyljEj/mf&e󄡈 a0M)+͒5ZAc!bcS}*߳ӄ[Eᇶ:rH y q+U;E*Ljx )- 4WaP)\@5\ØC7ɒC[s8"]H/UZ-*~<^Ko]*;?)u3kc2`WyF*ZgͮjSڕ8XV捎 >~\Ȯi3 e50 %J_In('X =eȅ󼮄(E* +N+ KmgN,e["I?jO6ĪA pz#G< [NPĖ{r`]ˉs\wyq] pR{rXWǀ`IG!YҭmR$AiotzkcHX5` ,"/񲳾/HJ( {v#mA)owꁦOQ|q>p-Ӌbit"frK/pdqLUboj ܥdY9vMM5u Bh(@E FkD3 KB␧1޴Us=(Y =F '(!I:V夠{'=*ZEda kXop;}Ej.Sر-t)؉}:;}1H 'YM$;$k[7 & kB0$ߟЊ7 endstream endobj 111 0 obj 1696 endobj 113 0 obj <> stream xZˎ+7WxQRZ.@f70%ztwkWW)yD\UŞK:ۿ~:f.9?{ 2"e>?#O̘KlϜ~ҌkmӅi<{/a.ͳڡsV,ev̬yvt~N򊮘o<& >~r5` W7f&?|njwojƳ|G~u?^o?K;Pe"e{ Wj@/- hcЫc%2p 㨰'k*t+ng{u ^]bV^.޲j3r zqsWk[^+4 :zgƱb(l0=sR,tP^kz=Wnb _'r 2 3Ђpo׭#I= E=V^'@ j,bF%N:- {Ņ%`Mm!N9͂{Θ<SMy5Y)q,/9}_$]~߿K4)j5n<&;3ۅtay-A棬i[S36_=?yGkGfixR. rOs%̶$lA[aBͶΦG[] s\Ee,*|9p.D76bFDTI$lI pg(vs:X\6UUNY]tu%Dc)]RUXBoj"Ro Jg+Ĭ#oj&Kj#?6[0(GSN`J0'cឫy`2G@R8Dx= ̟;$4GkkEi˜aQƘ-~,C9>yN XN(Cbo1w(J ^eZ|%ч@PʐF+*e |^&]5]شhU-#uK{ڥl?6Gx;/by/}I`J4-OnՇFjTz.Ђ#%Z|d l箛Cf5&". 8PYEw:(]Hp&+{5;5rEu`Yּ3`3L=Bjˢ%KFnIV0jm犖dV 14n J7[; ek4dO׵e *Uw;BOzkA!Yv= woj4X~XntM7>m+(>ę9oLoSІaʣq+->cy߉:Y1vnۦ<^1lԊ4+g:a8ZK7[=e 8=jeN&Đ t].Y{=%a$nkŽcC%9>WJ2?%YU5B34!v]CPsK{}lx x?)G@n%ECu^|~Lj٧V떂W9 t}0Xb\fp߼-y}kն0M@A==<,%FQh #Ѩ%eA !ʥn@ޫUaN/ PHL2ؤs=kX(3B gl e6Ic&FG<Rsg]_fT=j۫LӍ0_kv";N=zY; v'f;v<-$!$$PSTy5}yFV;t4xtp.oJL8m1Nڜ<ο07mh#[qL7炲Y|7ʜf{=KcgzړhZkپV53>OUN2?EɪL)JnѶpˤ ʶTOr endstream endobj 114 0 obj 2493 endobj 116 0 obj <> stream xˎ#_HԣÀvr[ܲ@v.CJU*lv0ۮ(InO~2'4{>N}3K<t 6Ǔr~O靁?~nx]C~b40cLLb7Mo 1qnca{ ȟɟ>}jONb^3ބ+\L}bp1|1w~Ӻ_{~Lx;G0L>>EPY-\zuRHGD9$@A 8^z7 A,6#6D/\~WL#"Fzsn&ӊC &9/svl:?"3ߓ41SdD({#9|4W] X$3H+ +Ġ<57 .wuy̸_ފ֭ !NBrb|U*ȃ"BJ.AɹyCbalAPp]adcD]!=?}=*h'UZ8|6zv2g>de|>Y1PW $*$rA}vIy3ve^ EYvqz:^I$Qɤ` ʐHNJ)W8.[XLUCD(+WzU W}G(DkJp-Tm?:E7dN>J Bk@HF;io/-"f/-`"a ɜ ZDA̯w _`]unP}\= 7+WM̯;DG JW+cWG4>oDaKI#.^L̺-]v Ibkv6-c!}"t nQGҰUugW eTQUI:BNdDY=61&};kJ6^NSOUB%3iZBB <&U .3 ? G{n}]Eblxm*,eW-4V^{ bEI5zu]CH[1O"贽q7*d\AMgਕrZSMmyri 2 P9y9motQ ymBflЅty|q$4᫫{lKGu]=>fC#Et_Vǭ?YT=!rO^8WLQ_95ykMK"Is:m|x̿ )BE~"Ω_?xw`\]q7r3qs£=+! ǀLO33)d(&gs7~bᗎ€E7Â5vv3fH53,p;Mf!U+t~9# G9Tm- +..KWu[^Cky=?J6J% {y~Rwkg5^a+̞dҐrо̝ ] DKXRA(uVjK'gOXvNS Sy;rƪӵ3:4e"r3KͣJKHEGz|B.<).^dS$P,oS{CyBn 0bS;ӢvyPAz)5UvdL=@7h M,,A@6VUEIJD֦̑IKGoߙSͿ0?D m]3= >ϩgbkI])ܩו$SL9-?%>\bor.m[ @ClHpLuyX&ELI֬vGE|΢5_x(:xabz-5im,W?I5m!.*z2Kà3UNz9D_6' 哘*2p r{ݑ l*©!z5ZTVGJ_I}5qj'wA-+l%/TM2%ɫ6RslUtH|t=Z *SC)dVF9< E=ͳ5ɗڰ4NsRӅVMU\EؙViQ\I6CCMyӆeU2"SSNk\A8drgӤRߩdY{j9OcQDwE2S*43|7kC`;={$nG&Y>dIR+/w=F*9]|/j3F} >p_U!&e?uP endstream endobj 117 0 obj 3138 endobj 119 0 obj <> stream x[M3ϯya؇re7C {OJRn/, R>֜oa g{=}>y1?y={ _v4}y[;py/IvHcz ?T i]r}aeZXȢ%兤1)違h^a ~9)ʼn+{& I &zR3r-S&k)IY8c߅l:!Ă_;x:0[əf#p{ f֝F>Q#fB+Fl5˺b뼌U$Ļ;.F*(xb\-[,VqYv?uX!GR3 I0{X, La&yTm^CAJy7ϱ ~T6Hˆ]O \f7Zv-?iZ9ap)xl@t3&l|bYCJh`5]&*$k) "g*dURJ0#W$ipTmc=a%rsDqV^1UPOC(V9`8(A֞c䚦(&[֝D&3IG+uudIRC"wNx*X tJ(6~D.< [=ML=o - vp=rϱ({D48M%T̕%?jiT?9akx<8.3BU]0C[RT:ϕ5 7`P-[ n1ڻji8?cxZ<`~~ J憜-sH+׾dq*XJxm_6l<قoOn+z{9'f!挽AF<4MZ]x91-I%yP:Q v nՉ+| vJ1)z{x~=5u<=k7ܑZGEIYbyI-vS=e^^vZv449qX)^@z+Ri{IɥoюrY[_k}섬gzPsfW]n)e8,:tx}M9 GUTKBq5ºDiIlł2Tˢ4v! !ˢi! L PX,w/cIQ]DMꛏ9?#,2rmyGVF)Fd?#j>TӿxھWF0aP+ N_ʝei`)̶uQ4.V:hK1Lck4끹Y14Ű [TyZ{Q\fr'8;ؐY+#fI4}^$ڎYY[q8JdJ$Gr X՜hZwWYNSH1n:-VE(a=to&?ıc3^TXd nCwӌ1k~JVTer&!|DgT@=g<œq`3!SILJ^$'QaI1 V{PF茪Q8~"uJU[N5K8m2/tڬɖ]RIZ!\NVU`gۓf8 ) PWPʁ.d z՚%Wz'1'bn>=1ta! {9' ^^:yR7ku]9YFHRd^a)(%5$31 @kmDg%|XxYά0*7#y4O*.=)(-?& |nЮ|IJn#_YȥZ3/NrHAI`Jrɰ47}L02])`M\V6dnYɜ(R!}B[̙K|ᘺ.ayEHֻ$=,XAR 7_7'6oN glބ<__3d,CЈa!iC_#%L\ ۸ף]rec0|1oHæ_#t6*&7*ӑ=P;J,-=Jyܷ\پY*s#[)ՍB2J ]^0Z uveD]1ITwE&Ucv޲ ̙*"˔0䱶dxpU1^@@+f]KQz7)SSm7:_ICSl/:> stream xZKl @w,y(Zd7АŐ]fY2H>>Uw2\}αdIsR'E~)}OMNͺrʞLznaV'wΐwVу;WoP%QR$&Rk$`֒(X/%^/%NI"{s/8@J+ɟ wVm/pV7y)?獿_׷ka(4) Uxg0 '>^1!#=&Wْ݊GQד_XՁ%b M+Vt3Dz3$JxDk1;фp%Ɍ"x&~EisuPBI19_Gd~3ݰz $LpK]5iX }⯊Ne(hV A\|"zt'JXpUl IwlIkuv.@.f B3w|Տ-[',w6G1pf ҙYC4z!P}.` ل$˖uk鞹$Z~P1Eu~TGަEl| rMB\ |9I<(MD"Vh@ UqoIԑq;loag[h`z=&EWІ9xY%c+ mLXFdY\vM1.(rx$g 6鲵 +7?IYJB'ZAh܉RS7 =r rpEn""h 1?7~%Jvi2|]$"aNRk˿~V>[Nj{"O#&)(蹁\vՋHvZ偑uAvFu5:pSh7<` +6/|$~ >B@)^ww㎵&E}Uv(Z珵kgTz+t X/JOSid X/$@?H+.Pf}dyt c|ź7OOȽ#ہ*UPSmDr$كl#o=ťg̈N7M($f  wmsm 3?(%Rpiׁ<=nWэ+fM+MYr@eY$}_'"4O틃wahߗsmxHBp|@vnSa%͐=OmΩ0mDzG{?hilHӿ[M:JR)K AR ղ$ Δr>M&&' bXZ]:f][_WB@g#g smH[٭fn;v8LS޾#>}aMImwtjXuU)Lha"~ɨ+̎nw*ʐGYMHK(6b3-wڤQZj,y<#&e,#ΚIZۅ39?4`؆o=Fq锭ƪY~Q5td)hc2 th st!:Zi=l3hs򐏨wGvm@\:c |ϢUp/f4XiM#K'bӳڶCMQ>L]O`烸 s@T UC[E-}=fxmnm@(B+_bPihؽ3a72vZr)ӌ-JgAb4 >nfQeH7&s{R*˳D6"-kKt4+8lʇ;?jՇi^5}M%&+4 endstream endobj 123 0 obj 2783 endobj 125 0 obj <> stream x[M# ϯswJUCn C[6r)RxڶJ(񑔗Wo~Y^+տlѽn/x}~Ƿo޹^p/ο|_[KXnv?[.{ynt]nuGy_otK%ޗ9ˢ".,[hVJ͐2Nkpp8 "8NEIl+ru+h8drQ`};晽ÙIxD|u,޿-|6Jot`g;L0Χmy K2kt=N^37[ƁU`x?a= niOs/,2~έT.hX8v'hlqfuM̐yxmUrї&LH\qh & ПPH>@ɽF.zlHNC,*,)2Urtv72A"~vLeoaqj@\|0byu@}1wXefsy{]liYCiAt64}_T;{;guPʢ'87v`F'#.hS'N>͑@3Ĩlx.'cxw6Z2H*&YkmX~+Xuf!#цJwn`e*%Bcmb3w=Ǐ,KrZRHւ$iȣ-'6 >O^:vhlVl+Bfn~ Գqe|?)pjP+"QX' ngV*R<ѱg?;a,>FTw;5 0TYT^'[Rmk;"o},Ccia'1ƙU!$ڬ!tTyos M1(Jb p^ُ z5 X7ѱ?/SX5,uM!+dNꪭR4P TJS u:%nb?B-v;'M[RDm;HDp?N<$ihjB\,4c3*Mo@+~ ͓= -~޼eaG7>*aO.X$ `e׼_G̚ %0 }S&a0Y@ 'XYܪ=]UC[œF"qj \DW?ٛ7,,@%4m:W$$+8өcQP1=IАJɟ+muSD G gg@˲vwH6,_C-פfB.Vݼb) dkcHى!ev- LQ*#2 |wj$[%QSPN4fCWy )P2 QE&"OF> % $ f65RLvwH0.;'SA #)H Ϊ1 3f:L ^F6wx!1燠B9{tZy0s0xZUȥNmh2:]Bd왟 :zjrӽL +2wT0A=ּDa^R`}7 ag {+y)s+:͡JThr"L. X\TO=+gÂԳ.>x`L *F%h BmK'\6$&u0 Ϥ+CЭj4d?8:@56=`Xfյ~wfˎ=ڻjd bggwwSYc $\Uo%Z2 [ kTwx?V2UյfC+~QIݏşI}mrj!n[ku@-X0Kp3T-kT (ج'~KK'>+j]ı"q-tu֩@HK)'+Q]j&dשZq6'rmBd8P/5#OKtόR~|om;o!H\~/{3h'3IS?5嗔j//9 endstream endobj 126 0 obj 3428 endobj 128 0 obj <> stream xYIF 9I 7rK S6@2?j7f~/ '>s;Q,Btg@'~ンC 4 ,g/ag 1 7zoo [M`j Fo%a3(1)AmzAEm _ #ew> jsk\q9fdƣ%65(fJ!eo]tbWbqVHԚ}/R7 q޲[h$/9RA--I=RU|]T&'6o,)OP5E4B+۔Aq}8 @hf3`Nfi¨S9OvxŴԻȔg|{hN`2ǖxwx;!QY Q[hR k6T WKXjaM~J̋JPq2\!;897d;f+}/ A~e}nfXi}mJ~0Irl33֕ȇ#͵1s%([rgw0;^ 65ؑ6}y5j&х5r}Sʀh k۪v7ˀ`[2we[/ AM,8kv *X,̵bTcgUC4pj k/3a08+c nqSq:m>5iZah ~R !ScCH'8Y =Ky? Z ! \sQX GHph t6aBUFo2}}wȣ߇t>t?GSJ`XH.轅|۽׮=`)ʎ_cͨ,H}/dy.8G;3joUmzq CL[V=De _TژᖺG=VJ1RjnC/԰nFm}n(AnFZRLxhKݶQbeO:,sH?"^\4ᄃL yjJ&-G/[jirb%e1Qk9kb N(=[_[G χ\:3!p,nP;$W+b3|Q֍W ȇ/* endstream endobj 129 0 obj 1726 endobj 131 0 obj <> stream xɊ6>_s;b !9ܲ@K~?dv#<l^U)GŲ1>Ưpad#4bi!bذD)&{,pFw= s5),'DrzG`5Uz"z!_*pajk*(S25-Uqdę6=M^9(i@r-4y{E=X^0w]V\œn)RI O_Xu)JuU+WLD/=>Uc]^p)غ@~\v'c>T8o'Xe 9u>%% 0QRѧe'$*z3HqR*z->ȊĿl$n6P]1\ S9IvA&f<*T+ٌSTH<8Mg$CvcWRdo-O[6hf/8PbRj9!$$u^:7lKYv$-IQvg71 n rܹTb9p#tdʮ[q [UݤGZ5cJ;Ɂ)w-X0Z^QŰLYX %yQn so-K; bƘ+&<&wfi7 TE2PnTL@-:N8:|~0Jw;a Zv-B.QV7bzg2 eqbQ%7e(8<4o];PPFۀ([&iVjV͇ʼ UBݰ(=Q:E-*gVDn~5qd C,jN: =g7]6齆,#F YWM]^ {hfʙ.M ΅u ( I6Vӯ*dZtW\G?MW -A3b5aG K/;6 \sy/lT֌V*?TJax× ?nm=ӻIQQd{!qւZz*(]0I'l|XӐ.!&lɩ)3i"h=rkkxWhsƑN.GqOƉ{T@F绒Ϗw+IϱRBN\}(K)XѴKI~8N}`W`఺6cJ3c_☲bb$;ՋW]ǾfAi"̋1.|3LtvQF{*"$RcAY,>PS>Z%L2X>_n/b(ϪR eloNp#me#IYX& 6jc`'IРagpw ԓAot$cX.la)?^K僲wuDYI%{f> stream xZI6ϯ906r{0C- ȻJ*I%[grXRjq 1A77& i ˬ`2~Vxzs0\y^=>ح͚Ns_nx:^(]Ïi4E8/?\(Ue[UH__v˫e[xpy}^/_6r`uX`'Ǜ{LB#4Z{' Hl$1!<`L(C*7X ,+3-+xFGx lyS3>#˫^,PjZH-F#;'LDY2ub2iyzZ`dٟG] s&ޮ o@DWQZGB7(J5̜дs79uXJ1:F9>|9Ur=>ܡ>sѫJ!{qs3jӉW[|ν>ѫ]} >ծ>sֶì"#>曀 _9M1' z$ayR6D_9En"B'o$a),Dx=b]H^,*HWx^Sߤ1βڈv$φ0't&UbJs ʼn:ނn @TtJ"U%.TA-[K\d$l$GLoU|M̎iI(50Ԟ-T&&B>;{WzV Yoa<ƢbV ;˂ЇqMX@z% JE?S"4dr:ԅZgQeɍ ׍B eЩ]?\ٮRqXu$5nR-NηbKpN6|MkÇ 8$`XNcKh'.E5^`[fhurQ?ݍrÃEi1{̑^pY5BBRkvm+)M3AGyǦ΂x֑vdD@t~4{*QOw{&,`eJeOs}!꾛;"c8hA9;}'t N]%ѭfQ& g.5׼OkXd=,ʂAԤ1D4q1Rɵ)2of-M6G=#ѳ!'Hf wɯvLRm1Vk }hĥg'2صϮ}G=Vk=ni2'bt`&  OY 23\O#<ٍEpCq0ޔ+9ѧh?sz f`cXIU' Uɖ㢪,-mk*"O6MVHX(zI+)Ke^jl$uW/Vaq&}:]!.TjC!@,%~qǍQNW/xi,(%NvCxL]c|rH5Jz~LY@NDGDS% 4I5vdt2U43%c $7v"XN{ONܞjTw5lSy#.%Up1F-y@Y~j)Ȱ5?R6_ \!䜐|$Qڞ]֖+N+n\EI8ڤbx`R7'b.fTRʖ:”A P^>N%I >Z $1a_~zjn/f܉WIŒ)L4c^毄2:xQYV\πs5h)N> stream xYF)tUI`CnBn \JrKL cWw-]WUmuß= ۗO?4(\oi;ctO)Y׌Qk+ޮn5MtkP5Ӈ3=@ޛk:J+gOѝh2g5+{ /BVd' _J<G{#$ٴyMSd;?5YLd1Q>MU{x fFפڑt4K:gh(>&cFC@8 B.l9G p v @Z 3q%~7sՑ=m5G4CQVyj }Vk<)ReM^In ]4;!"!kH1zjJ}x۰`в\.NB\a)FE,3'wd t4A^Fl"w0cĈ=~.$(S5EC*O&% &`&D l7_IT!."!E=qֆ]3$2dkĭol{O%=\[&xpiсN}2;~YyV_wK,+Ot8m ÷18uR,alBD/ܛy"/EWd J F=5L9DgMrnH^rFDկ)]ךm*(݆P>>π-]ҹ!\_$<:uyɜ(j"5 ;;2ݦAn@uiߠN2a0f%A~>j٘S+T+7% T-dl.f-?㳡]9Ћ޽2۷zzwAؑ1 wzWa4ۑy9eXN~ҹi/.P^ 4hTTF:ZVeƔ-YkS~r4j/iKʩNWm+-L3nfX+ V㊮Bnnϣ| :qiq?v2kkL~q2G&dlf[yu =.VKq31#rQT/{bVy( 2'_^JXd}]cٴ++Ch}+?q_V^;}ZA, endstream endobj 138 0 obj 1682 endobj 140 0 obj <> stream xYɎF +t*6mr@An C%YVP*q)q+;۩N'#tc~|;砎ҽu;o4i[KZuIc1h5V~~i{~uM*'Nvx'uI];_|n_L4*{+' RQ%UF =iH-!NWvmq ;(,PX'^#!( EI䲆4rD#ɬԘ͞El-M\غLmm:FX:ɴ2 ꌵa!eg5.s z9:'. Bz#cfѼJ7U49O׉^ TO lu(eWA=)c7sWĤL4 >J.<[%ϝ. 4n: ޤތJ8wX^t5+''M Bm" h 1 :.9lC̱nyk~_4}31Q+2Pif Դ!%> x :8;ۚLVd&p$%j%\Q:aPP> stream xYI6ϯ9dCLۃBnY @%?ER[ϼ$ r_6Wg:)\\7z{]'rq^|7 {~uo?]5nՀ&j"]uLt>\mN7^>x1ˊ5p ;#?FDYz)#&$wV(LLuv`ܻ} Crӄ!gAj=pl?1W?}}>ᑫPHaӜ^O5\`9⑅aoIŝB^ebلD dR@Glӎ(Oĕ`UkH"쁅5 o %EXlxMvÐ$Z:$v(]Lc K)U^YA `HdKy@qvV TcmÔrm62 Q> 7؍cs?`X!p1!H`1\l\BArQ HD'U2[.>E0Ӱ|Rc-DOE+YewG=>/AGr}MqNb9L୤%d恠뇫$Q5URA$I|' E ZܵMEC1;<]M&gq@X 6TMPRxSxC|%uԌ jk/iVM,ԓa`zF^bEb B^Q+E:&[CԳ\חulL){҈LF)UD7f L!}zrjSdܧs4/bSAݪd )ϼ* `T}Kk%mSDiR#ےkQKѵڣPQdm2ByvVܬAiJFD3R:ْNQ>z/)r(=[4L 5@M[68EBjQM#tOD:o澖 T؝guӞLViiU<&˰OD2>7]ѻ17fejK*v!MCM~*>ɮwCUJR˥TyR2\BQyQ/k-lԒAhiG6zV!)7ߖP]]f7lF|yZܸ79|~ -/?' endstream endobj 144 0 obj 1898 endobj 146 0 obj <> stream xYM6ϯy{UohmHn 9@`v/y%ɲmyf F-{JJ9z%h{pUU3I`]o[lҡa5~*(B/—rP+P-@TQ3 sObnx%G> `-j@b)Ag.q0Js2:uI{8 GZŅA"3 E3:ZW"oB藀AH^*/ȲFB,loyI-IfoUĻ.5/VYNV G·PiC!wUAR}Fh-% )mn%.vhc@[%u.,0K 5ؒC騦c1OߑVN"Wܮs0'a<WKFϚnffxiIt*-K8lwmK\SYZ;~߮2ljWhb زxwM:2Η~?mg;|Bݵ"!ر eEf=ûbJX՞׍JiEQ̕ڤ-+ىnX;J,r(7U5!ނ;f[j{Meۋ}bپ'gU/;5ޤlyК2oQv6P\MA'\%$ʛ"I:eEtsrJ@Ǻl߬|]QePITN͉@ YU-b]*] ,\R6Uj+W$K' n}̆@Wڹez4|xjy^b e6M)m( xZs c?# mԵg8 qkV0k=V_r|YŽB"Yo:ُ_a4*n|N%Qx+M*{ n#:%NRC%U泞4cD}&*G^954s#z un$Q&v;85$v[NTZ6<=Mˣi-i}l3~F=K_t\ri,k#S.}}:2E"q'.xL~zS,nRŧېa9a49l>~+D:=}WKAd2g endstream endobj 147 0 obj 1946 endobj 149 0 obj <> stream xYK37_l0mC \bL20/S/mo…vKǩR~ǷMҜ0u󗏟~p9O?Lɇ#2/vRAqo ڶ3%Z썔0J:pBpOO?a'䂋.2-Xf·trs=+X_i??mTjJԔs:8hJ:yOIv %i= %a,DW fX/0".p!APzܡgA߆>~2PA#4T(+↋%*^pHvҏ:9a1 !8DH=A=@X lE Aj.%JCFy!Σjb>gwstQE!Jj< A2ѰSy  EzŏwytK09<& 2B:Q-cy4?8#9c4i'& Nh Hncp>/Ld~մ F^ vbēT\F-ҲXH:I8SHzmCIYFmG\Bt6V^#,HDHo"p@PJ:DЄNE9 ,qX)y nCIH.ӭV`Mꩉ]ltmȊ/%ރ!֨Zұُ|xZ?|~}WR8 Wb5 ^JJLL 65'E}ec`h^^g~?  ȬV+܉e /Ic#`a䠄:h+X8j͢ 9`&} 3(}`wD7f@GvR#;7M`W SE39#'lt[2@P֝yg%t  1_Wtl*Ћ̂'ЎOFBюt~G8*z|*U v?8{;]"G:PS>2 .I%&gaOU*ΫAT9Jc}̭6>DK3{<wVa,-GISVUk֭kCk|IfLx,9VeKhÖg9Wf*d>I]i5hVBϣ"t"!* xUN߱,fփf" Vl`gYk[faO,^#׶pzfyV xB1TgiuM_C|ijxs m&* AEBQWV|L^;N{6^O qaaU[af!4s@eZG{gn6ah` Gp}iK wz[n9AG:6Da}/d )E`#kIT4=^7sJMgk@ݻґAl9+n.DΥ2 `#fԓɵm'Wn-^z3b <w+ &:*Zzv <]bdCFY]`爸m(H tWۤo/ӡ4Q|,ga&Va͘< z):Ҫ]Z7JZG.Hro8ݭ SZ:@103V\!xƯΞ[MʐTW$$Cn2D}_6xh7og).xk鸳7%Qd1^s@O̝,PME[qg~yzMM z'ᜇPs4^hޚ7Ao$qVүl̸|?_평d&d` endstream endobj 150 0 obj 2268 endobj 152 0 obj <> stream xX˪F Wx=p;a4~؋e&E GUm77.H:%8s{ Ld< ?~2us ~8`y4g6|"s> ;G8hpc$d@8DDܞ'BdhO+H@/`>i鷛?t[Gu<}mDf:4~0DPV]Ъ\^|t|׻CȂ1MF/.x.3$ɱC MERzȻ}^(]^(=Ed,g$X1B#*&{K}PTVI$|Pt4̅`MWuK8Λ)(8L",#h_ْ_vMZlXP$xqpW@9{fXSq l [^a}oGI۠ cT>ʓwvIsĮ[ٻ>c;F|93#+'[KT|O#m>k 0޳ff}ǔ$.0&IC^Q_4'q5ΑP6J2fB\.k&+Q'(Řk ̙bLS$<”+Q4VZiȬջP X3"ЦM9FK; rgdWdBP()Z.1D+Ss(b !2P#Y8X݌|q;E%Eaָ[ wS-܉u7J`,ەC8qn*ΦhPnshЖMA0St\ Uc,QUTJz $L_Hme^GbjI EzOJ,('CQr!bSk~Z{cLhk_ޫjFRu9,"rs&6Ta}vsmuCi3?ڻF튀=)u\J #]eދDrL(N T:HjܿyXK_.ކ^V(EVJv=R)?cdvӈZFg ^6)Oq*_ЦxXhmbezo ܟ8쏙×sh: ٛ endstream endobj 153 0 obj 1475 endobj 155 0 obj <> stream xZ3G): 630c 8X`M.V_=FRw7?eNps0O}d` )w%8M'z3g3[Ox槉xx&'>&'O[O6EѸMd6es1.D:Fw_/8>lgc3gkfrgsɍ\r?x~~8;+G# M9/Ģ)/ NCg?aiDx@#$#$A&UwcB3EH"n^uȩ|K!}PHE+ 2Qs, &Bs Eblb؇+TXz ue+8 bN\N~[QEM$*LrG:W1 HUvW4!IfSX9ZF1sA>39.h(\YѴѩTva?BL::M=R,:9Ú!\ۓ.t9Сovr 4EoK 'g$p ʦbPhZA}b%F3aZrkA$6ZٛZ,#}۲\f;5pm8JK֯qWhA)qqQkP.|[훫vfœ%`-WJds7̬nt+kOsd=Q9i"6)gdZUeV% IoaA JXX) P 3V*ѱxWotq`\ˌ P 妌=2J׊=h*@Y̫,Çb'[WU)| XJ{WEQƹBUsҕEeM*MֿTY/_(P5ڊ\*)(V0y VW ]cGB6܅CH" q r\3@xϾ6́#߰rgЪFJ|Z/||UzeߨgP;͟XX澚s7 D/|R'߈mmPr=0gtۿ)jsfolkFɨ;[wt=^,|B!ί;GK_9e*kɒwYV Lѷ9WbѬ66 $ 4^ԌHpg.OqFm*F,[D*h?QEͰPiHP AQmd[{HmD5e?>n& ݴk6`]̡#ΐn 91އi%+ "wL[`r#ZcW~̘6tU',21b:mbuF= 54YLmSX ]ƳjU`>(JD㫋[[[c\ooD5 Jo'LdzqK0>r;3_R-Q\2-{,H-FZGz0 J6v /)<V3/e RM}27k_y[h( 0:r~8Bq̗RJ6Vs:t`E%|f /ciA~PL8cc.iЅ=#\3B?u&z7U Êa8txK!Ujo%fm"1w.W TpQs4ŲԬxXw݁bqj@B#^{ gu˼Lk7|pw%(C]> stream xXKFϯٰzC B H 9;p}OUuFQw뫯ZWl|i擿R:׏5^Sh56ʍkl|l>+{x h/FY= p s<HP PKPH=_,'_/xuMyy?cԼ~_KO{Cɞ};nyQM/?^ˇ' [Dӌꇸŕ%_ 1P8 Xn@SA `éU$"Ss4h FQDn$hݶNJϖd,4c7cY .poVBQjJ ]T<[.`t {\604w^jo0dIDF\:'SnչZT>Q fR.IhW62T$ Xt8)*@rIȨ4R+eUK̡UjDa4J)!y~DF2h2KT.)8 .uE$K{nl(#J4BgE=dϦ0%/%7;G/ _RH֚-N !r+di*eGgtάW :5U!ҭA8C\R"iM h P_]+dIǨbw:83A#N@8L8`B'qp7J:wىt;$74f7Qy`#E{W723//O<ޑVj9ZXBY"IqHw:uDh4]IGUN%iIph%8qG kavss/y97K> stream xY˪F Wx莤z`Eve&E Gv J:R=.p/7 8|z&yp@d;iМx_V UEQ<"d( h&'o)PzsoI`_ٗ+Q#jtxOY% 1JE&q /p:J RxNn, yÃ%/Jc(E2$[7>HwjFѢAo  h5$ o?{ /ocx9t 2M8lbxM搱z[Aȿn$Е]K[O,-Œ'ܗu.kwGs5d.:̵/"bFXl+ .)&.sLVz9URBxm2:~)Hr]Id&g\s j̭y<ͅM}VQO-ǭIύh+q,e[.b^2PW=Z8Ҭ MVf{1wPaqx(T~!4׵DV6^UqgGG3UVfE_HYi>-s#ӈsOdw ZtGT,lco:G鵳eoa -G71p{&S9Y:6PhD;(g>4TՋcQktikXk@+S[4{Ċt(N ź7Itz[IIn$/цlUB V^[]tف:J XP$׼RjshO9ۚio.얣:Hz NOFݕGM,g%m-DةF5m*֨kv*l,j40=U,˹Wmht0 y@짒Qص楤-RvoHtsrX:k?]4\t4,;նeeYDw[ $^m.8a&TqѬ̺-vS+U_Zin%]BZĖk}mn]h6e *XV ScI.JO/OT  N>S״M-@fV@঱[GTD!}tImCkUiIMeEBR6*ݶB<-\.UpG͔JҙL:/؊ X Qb[SA6{J4X5w9ćr}JmyIwN|HY?lmh }ءlh{5z!:Sa S0i8QOI+jz/Ҝ-r,&QW@8qhCSHW<\{v /HdyOai̫%lPL2O19A?'V@ sGmaX>[ВL֗D{{2wg> stream xX˪#7+z=NU4Xd7`"d,M~?ң93-TGz=A k}O'vۿ߿]gm=C~}Ɣi5As1Hu5#!ێ1=MMӦvNEiHе#ՌD q+2&B=009=~y`=>MgA3V~OvA~lFL~Y^\yݧ?k7Ħ8 R(Ɍ+O&Mn9'fٖƒ ubB#ާ3+4M 828)'вcL9aITC8p80ZOÅ0dds=0VN\{gq0fkkJ{)r C׀΃"nTXG^QfF8x S+ȩu<YDq2jN*\̃*F/hQIlQCDi}bF<`@q$\yB9!T<P0 00|v2.9Jk/`}8 sg-sY6z&$UIJQ0/Kl`3rtK >h,0D[DJ9K-bOKXic["/LkC+VRL>`&3C| OY>:2DO >r,Õ-Q)q5XtJ;D;^QYŇ\&a^ oc|P?x"~'#:X>BC`-EC@H~]?Hig*iۺ> jjgi7w wH `cDi\Ji\|jC<֤K:?Hhmq43u."wS?.Ig]v#'ɃD(.1hPu a(O_E[Y]Ikvf%m^g*+Z=>/«* ,o6eV|I$H$#6W dZ^#Q*'Q@^SB c yEM܌xnQEGR(Us9K(vkntQ>*"tu UY/d0_E/9t.Scb͕eJh=.@+1Hzȩy&uحx,>wl endstream endobj 165 0 obj 1231 endobj 167 0 obj <> stream xXMF WWcH =޺C{߯HFz$=#Ɇ/ 0]hG5cicn8~vn3at 85]~2|״(Mo m|;Q5}@>IՓ(IW1eR΢[iF4H w|sg>YC "PUI)/XmT ,XIP,H tMhAidhLn^&+vl؛Z&nZpr)zKζF uӪ6oW=Q0l1Y: KITQcg8ѩK[ؔf%BƎ,.5uzlu\ÝoO>:SK{-'XHZ2h>{R "i⭏i*J6 󃨒sՓw+/xpH+QVBX)Co5{8fncMUX>Pg4 4d,d"N<ɷpNy&)q'!s`B|A-q,{4Q1d=׭G]2HKVSYq7+W &7X}12 )$[)ߴ#'IJ7m&B87۰n}:L/̭j7i:hD6_WޮUY鱹5&bH/AM #YF^kO3~K6kkYrdyK(4oRR-cpeS'[Bg4-ܗ'\9cʊos@,=p('^q6]01:FCʭeXt3֧eXK/* U% I*S6r͐Yt{w10$&1U4mΦJ#w)䑑A<4{N"MPGuy;)ĨO 32^<5ъ,sf-HF+ҍXݲ1ri#:hNrpc [@""[c6.i,JlusZoo:iN!=;ˬ^)ZJ]$&mKOR_⢖P=eA4m> ϘW lsljμ> stream xYK+7 ޟ_1BRK=$YtW8En+yx)I>Ki߃ s,\o?}7aۿu|Ֆt߆߿Ks>Μ<ЙЛj{sf|!cfkLw39C}>u>^?nTq|jDJM lɥ=%ްq$-YUhTn/ _MW$a}(.G,ůɌ$DѕD57> LR,!Ԉq+BOwz|C{! Cp57xv%O{H)6%猪Ǭ6b`R,\+:nP/|̐@Q#IiH~hV̟oa_ F^Lƣ1 -󞡿"#jbX.;xG# 賿2  .,H:dqQ/V2XpO{9x@w%OB~K 0rxe)πI@;g0@aap! @;N".T "bˊ@}iqG*BWQavKoRv̧y3PLZڬ;#/^U)1NQL R<Ӏ؞䷙9R1ͥu%.S(%_Jl2bнL%cP}qj\W,Ɋٮ]BuYSpdGz&=#M jX kܥYu$"(aDwh9btrD*q"u+[\-IM2h/7AgvF9NF3;o>_"w𑓂aa(*ouy^B (yu$B b[{iu 4d&N)u 5?x;屢ܫ)}=%WܒSf7GGGV{B!R\mR6ͥw{SľlM4Z%B9Ҩ7.pĜ5㧦@Q: ;$1g3*m)v{+ 4\5̗8_7 ](js=!&ΰG\*58Z?nyk:ugX#V MbKxܼmzoؔ|&Ԋs\.#Ty֑$@ 'O&Trs]֠I/tòa{tZuy_w^+t{aa3?6}cS[q#[*L[0]Z΍I }[",KDiy(fUWͷ)lq6Zlorkq^(F`jm&2qW 1)לbKP޳1>ME[,^}8~|v PN?'Dr)D<ڻ2ֵ^˸ԍ&ZZKS%jMY98^BLcG}[rTqoj. a4nhm$W[@ ( zG_.3 `_yYn:%?s5 endstream endobj 171 0 obj 1796 endobj 173 0 obj <> stream xYɎ6 W<@Wh Ň( !@)[rWO0*K|||dgl;@F{&<Ƿo_Np5uFf e[K#x?f8Wǜ2L,: ULLU&cUDcLLיWpڈwU&ʄM2au&TPeB^K^dLbKXXPD e,-~Ȃofe[W|ZzFjȻ9nfAٱx'mU-eQwxזbf09(w)Ӹ'K;|F3ˇdO>!6/?gO?!0> ܜsIܥDt>tHgB9L}lesӈ41iMh)ȝm0Ϡ ~o)nI=Z9띩g^Lr;Λ";+O=& tvaTDvlqoX~X4PspE~Ժ\î㻲 A߆Uw 6X;DvAڎZ}ǯB@DaQG endstream endobj 174 0 obj 1470 endobj 176 0 obj <> stream xYˮ6 ߯z"e2x3tQ)rb(8$|\W9v߿͝'m- ?p:g}sHwp~o.\R7 wvv_8p8]:870]xqK==^xyswl~>1LTt 8 椠9!5= KٗIv'-;ƽ}g@dc9 '끷‰FAY"y; Ci/K LS(35i:6@g$"yډOӼŕ1Jgx xN2?QTz}XKqV86H(z]'XĈujk&Kl"gXz[x Y `2vkuM9@c%rT &:ɐVf]qO!R} '@x>r mѓP}qr9^9tQzzΚRBnM.'׍i$׬0jeY1hެLd%3ri#$I>vQb3R ˨xcUԧR]e(S^@0ͱ4kէOOYt$]P[G±aatYZ 8>@[ Rj;xRQ|=C5M9k-%xSErZ]/7[KhjӆI~fRۜE{HV7VQu^J!VӍ[qũۮګHHz^VN٥B J5nlz6fڣ )ֱfS li1! ~e( Z-%ҰgCW*ńk|ڶCq= MG`OG9LrP K#3*lQ.qlvkFP;t nԽ(UNV@;g>=4Q\mgܭ + n0~Q*yP? [N/ƸݦGգnH%smaV1-S{.ES3%Vi;.e\ EL0LvVWӿ0㠞Cd4Rmj +\&yglضQB c;^ +.ԒO|%pvZ:aSRe#췽eC\&5-x U- ,1'&v0fzwJW!~kN4s185ݡ9*6ʦ_K2֣ IӵO$xN}թ¼*]i}Xr~ }0agnႋN?.8@6`/Fj-e c}<Ue驟@O?qB;%~0?34z*4x>ҏ;5կ,pWT w̿# ˵ƤYb{A]R(UW.۾k^!> stream xYF Cr^`eHA."@<$K.< v~sP[o߾t2[Kyuׯuu4c~4Qʌ4+$8-!'}HEP!0'])F>v {KIQe+FXavhty$}|u䳻XaTif{9CGN~}`< kkP"H?%u#.b[OFݒ7LOE,7GL5> U >:׽~U9>:wc9UA6 dFt )Yo3%bD!ԣHTE9#i q`DE5/lUCmEVK3g/Ld](0 yFݣ̲ETzYͱb2xl8dy~$uhy4@yYBuur1|\[ H!酆[8X+MV}|K6& &J1 Wx^8`;"Zԩ 2,ŵ3= i4\Kh8/ ȓf.Sf`p O@BDs,>lgB+*9MoMc,.Iq3=kz䤫-ōsQtbf`Ր ?r;E`]G3Bx 9$yuZz̑TZw||`cmǺik(snҠH zUnЈ7Z'@kM$ E^A͢5݊ImG]Cq S2aԎpH5|C soSPoB/dF7݉Ԭ؜Ӳsjf}fJ1DOr~vʩb-xDfXvw$.OAo"e F3rLStZZh1Yl 8[(I 6WlMoA$G}ٙE=|6{b/\8R[E@KɁe+qsV'y)Ō3gg\Pmإ&םDi,ҩR m˞7J'P8b܉)l/]q:XN;AQPjɗ<񼆜EĠPDGhW܉`);.kz)3AW/o)gw+ߑ<۽|84}6*9OM3KQ"uE#b2>,g[(oGVXBeL w i3Ojl=e;dhRC!0$P5L 4u[X)2G!]jv' rP\eM4d.>wv%{zu|FSՃf"a%WfzO^1Id3+bC4rWW>.3}O`aGIM;y_ endstream endobj 180 0 obj 1700 endobj 182 0 obj <> stream xX˪G+fmФ3tgAEI yX tQ]uj^w/_;)'ߛnto]}9{ 랟Vi=sVZgeS~1 _%jrz[YhN9*!t(~*jY;q b9pd4`> E&YߗuĤ {&#=D/{ >3?I}^afǘfx@(y:ײL;Y``'FAc9* RR6g4gSUa^QDAU *$eX삽 GWv9uV}a(:h`6Pa}KOƟТ41-y4>`G;QR9{;sR6@9agk,_OTL*B5B~Iu[m,kVjnY%0W>eV[BHji@=>v%QGes}{;{SC$P\$ uͦN -;%lH$:lYDzQTڒ' ;f;XLlZTߥ7VoV,]뽬jt2EzlM"!9ꦨQc(n`'` %\eiVK_IO cvE seLQ.2Htb1rn Aq  ^ u{ 3;t灓>7o*EfpF/7/t1>ʳuyo}f endstream endobj 183 0 obj 1181 endobj 185 0 obj <> stream xYK6y;R$ar[!݅K~Tz!Ooa-U_ꢻOtSd{}/>uePzpo'.}zx}:}ƞ$ŶiUS;.cS놦.ڦ.@[m]%h}1]ߛYw_V0U2ʎpU.|яݎg{U_=,<3|4iz;}^Fe[gF{զd(wإ9Zȗ=90T`0꫞3Q5Y a#xEC,X+Z <ÂprQWy|P=S't֍=R+Bӊs 7AC`ȝM_{: H jPyh΁vТ4T;ҐBD*iePVG3SfN-3 ;ɻV_m+ln Y OJCt8F#p2 ~B7mz;: ȭwlz/om}!o+c#bkmEm[ygy)ow~&o-dwS/Pe݅Ix4I#Z=$P{]@ʇ/ڰ f" -;|YȝEKna=n|Xc\"s#PGVuIypiƜv!iZ2_ASԮb5Xi`KV8GM5&,4“b幬?LҌPN3?)&lq1)^`b%Z"1nז2nv/dIE_g/Gp9@4RCb||V;K/}6j-:庑OPzM`'9d+GUZB͉DT쥴bTXVXZLo R=Dşӵe I K6AUՠ"o2?zU`qʚpP="!^IۤMC5BA"SUQ VRY3q")2&D`]k .M%̷!'y6m:#G`ܒSx\*3% V@OlH}# / fe3B%̫.ҩ=jCХC2 NYfi_)N.Pwj"< T^UU-^uo5DUT} 8uVU`Fwt4ʢ1&7#c/GlcQ z̓6nA1uPȉ;A.w@ȹ nF5ruouþg -b4,Xx2:w~!'fP!p%H> stream xXIFW7I mCi@`f.[Aݵmw÷uҘN"1|py<)v}oݧ:_&G\pѥO.cOÜ'7siry܅_\z_?)p=dtt0N:,Lpbwޱg>޺]C^38AӁ)EdLe10g|fs.,6Dc$Cd=ȭxM=7J=\) r֎9;7 hQ$pthk()r{'g>>a27;$'EߐEH@LC^Tu+:d>Ez:sr!JY.xƐH+d0[ EN WO(;7: ,M>BI(Sg"׮0u#""b xEɑ}שɎ&yHSc̪ V5іVxhҫVeNdt^^D PolVie[mJվB`y#6lN.!|UY7t2]Dgy`*KDyױrTӺ6E1: t)w:i818|S٠#.ݗO?~u:23ͨ endstream endobj 189 0 obj 1255 endobj 190 0 obj <> stream x šFK31y1/Kr4 20"0 (HL0QP@GVAQ dD`PAPKݺ9uԭvu}z B!B!B!B!B!B!9r6.Reׁ!..[ڹ7^4ld)SV|SBҥ{D>gefđbJFwkݶ)?򈽒>dhؤi>7i:'j޵o֮[붧]y;ʬ߰]F[m۷>7m>|}u܄] ǢRJcfq(uGox|6ruæ͛x (ƌkvE e*KgcX n)U,7U{`mz> g\fu6m-u}KҢU "فmч ir;ݻq2 \Rn=}+ʑkdyvKؽ{ףơSt2Ew5aD;6ā0k`K<EZUu3FVROs5P+ зʄ Duy* -ygYD*±R8܇0sKTe`"Nyc"Fb" M)JY+21H5i{d^KuE3UrjڌbcNt,WdecƏ,Ux,q\1}矓?qxjlW\j;O 1 qܶ}⊥=~?!7X[y)2ᕘG]B~ǭkVu5=x:P\ӓ9ԤIy1bo7q]0w,p;2xE^޴"qMjJXTZUU#r?[.ps_q ߰~CF-P':ٴUOeN 헬1DFf(J:8 rdH.qQЄ!]bd!ns݀1̿d~Pcl:歘g #G]]bR)bhɉlTyH}}wۆ ]U7+w fe.\²寊70Ĺ[9g}v]nyw tfm0~e`eVٗ2bg KA.Ez]B!$. !5$!B BQS.ٮ!EhBzNdRBH]B!% !. !{3-JҽGɺu)F8vy-۴?+C! `ss-)Xe9iK̑GOۼB3D 7AsʐEΫ:mТ)F8v)̚]+C! n2žȑm;>x}zs$@pL?8fˋ)uFC8u[i Ζ-Ik~1ݷF]+q0q Gx<Õ 4 s2X:o&W,5{FqqQZBHf'n߹xRaLi "G+'zi+9QIa^09K^Ci{Y;/j|9|y/߻n:X6 -GuƄXt)umgǏ/}uy=b?bONz&.`%Kn~}ś4Buoo9¶ %"%e9Whg_rjyab-fW]3s7۠4WvIIv|鵕_4G 㤙RqN0>؟K1b~qkK3y4( iu%!dedټ}mˊ":KJ'J!J! vuP uP uH0K}RRC]C$B@$B@ R  `hP P PCԇ:H(:H(:$ڥ>AB)AB)!.J!J! vuP uP uH0K}RRC]C$B@$B@ R  `hP P PCԇ:H(:H(:$ڥ>AB)AB)!.J!J!qaʕ4l$eem :5hߥ{uS e:P P PXP[[ v9dx8uE#S e:P P PX0a⤉HlծCuu5"jݾ1Š]uP uP u>{>k9Jl8_DN:%vAB)AB)!>l5k]uP uP Oh@!%8})_UUcAL  vIrvyLjųf(.21Š]uP uP A K:ffvd-n_1Š]uP uP K. R  @ҠcFڥ>AB)AB)A٥'8 ǔ) ٶ(٥>AB)AB)~tpNrhP P xiсvuP uP Av^D2 R4LޠJ!.S.%vOt]:H(v`h$L?JJ!C3vh$L?JJ!.5P]0hJ!]&ڥ>I)$Rh &vlvh@$B@L0K}⫃sN~J! H. jLuKΉ'AB)l4m`12(Rh &v?Z.:H(v`hQ;׃dH P2$Xi!S2@R!SvEhNGhB$B@L0ɳK{s K7hB$B@>;vݧA=z[)eem :!K%9t? E P mV!f {ZdImmʷѲM[ ^4{e\ʛHYSSyMZ/X_Zb8٥@@dS@|qvAB)]fxQA;)uZas\s- إՉ,vx@VS'72!.3HL M2v _'!jD:tNc\Tb`umˊ"٥pE!]/̦u2Є܁ @pvAB)::dT%"R!~vo>uta(J!Ș]X$TIKe Pڥ>AB)\.3Kڥ?qff3;ܥ]F 2e) I!S,ڥbHn3oP d.((]<3vCNkIuz{=DQB݄ ovI$ck[HREY97 Rt6$Rv2V"b+CAB)K³_2]:@Kfe`'>秚Q@$BCVX'Hݞ˰]mgB$BTl"+Q9EviO 2Bb2oP uȌIi^֣c!l4K]J7 Re /F,aF@WˣܵYvq Rv 蔲HvCV9h^oP D. 1]~tv2zRk:[̑s3 BKke@2ڤ}_t$ܠGH( vC4k}³.u&IjieN䕰SB֠:[Dbv.SiC/vjRbaF6ee]פsaM)m :5huSKkS:hΞSen>S3B@>+WΞ;K7 ^8RN>hcArNs}:hΞߠIG4[WI^/ eiԴ9"uFC89lX/] elAIv#Nz oXnD0l֔14h/RN:%#WRA\WCߝ3nON hqѣCF;v̠]5? ?_7 k>Ht"KbdFL*++}V:=B1Š]Ek'\hbt""Fd/!z e|YfM~LcYkjjf^b.]pt@ڥ$lԜe]jgSU2'FU ۶a#711פu)2/ gήLLɩ^v.XN ڥ$Tv)ɝ^ei҈[mR!CP?#Fg @nT%N/AL<8 ,ˠ(M(NJٶ(٥>)uȝA&ۈ~gs@S%Rִ1jgڥ>KIHv3Mڥ$z deӣjgڥ>AGM?( ,dإF6K}ħnMS$1=UΎ%tFzq?q.ex Jĺ Id~YĽBԇ:HKO!GՎog|vݧ]؋@H.R ]z.\iAB)AB)Az_튤R  C=>WD%9;N :H(:H(:;eή)Y۱ka&Hevhm0keB/v :t.ny:y:J)jjj@ͱ$ @kP9%B+-O8fj咒ڔW۶3!u gv ;E浒O>?|}ś;w1<)ص`({BO؟. jۿ:bK7o-Aq^ l+#7t0 c'sB }rD)wdI'O :$[fǯGlǘSQW'4%&Y[n5g)dPZuHk,}G|Q=1aw !I˞}nc'z>3'G8v91:udFq1os0gKa.ľ#jéHI"݋b*!LI"-ub$RW<#[Gk㋹_ݶeܘ;:u7Z ) kw bG$X$z !LB$BRB$BRB$BRB$BRB$BRB$BRB$BRB$BRB$B!B!B!B!B!B!B!B!B!B!B!B!B!B&d`````@d`````Hhg ~r鲥 o8_{u~IF2e7;u+*]gYHS}VfF)om+OO#+nα(ATi—|i.anYnt]y7o:ѷ"qM1r{7aֱR&2RmlEgXm,+ \M7yn1:nHQ;@@*U*±R̫XEo|pr'xkVlvW߷d+-ZPA,R4fS~O Ki=5׶Vr+8a[W^{%˷^'O4{GǍC/Ή3ӧI)]Qꢻ0q"D݆9B8pfB#>cQ)k>L2gΙt#jVoG t; ;2'? jU.Zٲ JC-Uc92^I0#<|1Uz1F޿o1ʖ7w; Ab^9E AgâzIa؏)!6.b0VDD}#&:Vä(S*4PCfGU0tXW+X,1͘!|>D2{I^AV/=6nxR*±uS,?L9s˻[ .çǷ]n۾U^qR_ d~歼WQsJ ݮP!Sg z?G a¬~Q]K, EC_]dN$>5iyG軰-,p;xEb6HDD64a:Vä(G1_Wo%\XH\*=bC7Cm:t%kK{,ԉe6my&`5hȌ\IgcX %;*00؃g4zKtM"v>Y!.3H4;%&-&K7NϘxې˖66p f/;h0S.ѣTi3;)Y `u]Pc2`؜~(Z5q+=?}W-wٵo>M\PHVg1*Wt,L.U0e.G lٲY|{oT|np0flNcI52e7 r9QްC4'qq+~0E<k^4{Q<Š̀7NvXۘr+ D/'H\xxD]%t)V1]E5l(5,{-],}?ԓfWkXTZ[b+#UWAsLvRjc8@neq'LJ -[ +Z)UȱE`~e71<gg/^g%Ҙo30%8}("hFn.aWŋ`BT_ǭ̜3F;SM2X޻4a}q>}jҤfWĔs ˆ]:zd,E?#A3p`1]zy+R(%8&˜5cEXvıR&21ܸT73=R_ %͚uB0Ecyf0 Xeȝ.ַ̾30000$2.#h 9h )]fze BHԡ]B!j}I+B!D%!^]]B!$. !5v!hA$BKB!)]Be*vѻO]{\~RJtޣd:- }p{ʖmʟ!0ṹoDz%H𣧊m^!VbDa^,]?O ^4{x}zs$@pL2'O|e2X'uFC fC7e Rק_{퇸Q E'L‘7ސ\WaNWK_ʖųf(.nԴ9J GB x.Ƅ |rcu 5켗Eoo-Ɨ'7kXfes9 i.(aӶ)_Z'U3GrƥxV:9z*R]Js` 35L9ۥ2r=B!1|YUap:_]ɧg.0nq9߭xǕ9* n+{~8,[wUODxN7nڄ[V26D1x욚Clf^eՎ] ؿ?+nP./V]]Lͫ[n\/V:9Ud}aᲊ]⋷OЭz"e\vs>8x*qn_Td3}Y!ٽ{ 7EJ~ţ>b9{]mPf*s+C$$]ʉ/#HDŽqHt'QFUOڥH1?85xR~B2%!N<@$C,mYQDgvIAB)AB)!.J!J! vuP uP uH0K}RRC]C$B@$B@ R  `hP P PCԇ:H(:H(:$ڥ>AB)AB)!.J!J! vuP uP uH0K}RRC]C$B@$B@ R  `hP P PCԇ:H(:H(:$ڥ>AB)AB)!.J!J! vuP uP uH0K}RRC]C$B@$B@ R  `hP DMn:MGMl ؐ R @:ЁvR @:ЁvR @:!AtvuP At@G:ՁvKt.i;"mA^nFԇ:H( R:.@4uHmW8㊕+kkkgϝWХR /B)SMZ4Bd]C$B)D/.RRdh@ԤQ戴jסn_ .J!(AmlkJD4)Nqڥ>AB)ҁv"R#G-q1v H@A6 ͶvQݷOlծ*E"R H@QhnZO?D1bd5553 /e!x"y$RM"v&a.mK68}yMZ/X_Z*.ډEҕBınv<7`uh)q!"!U>.ȉhoM"'i! ;U>˴.}(M\;=ۥɳK^vKl% 'BD] <40ɉ]&!>/T@''Bt] m!=HT/ /-Q_uT'C.]:fHk;i5.ݢeNɘ]f0dDR?[,]Jhex.]JR0@~ie uy5sfciiI"vei+ۗ@n+.MR-.%,iawPX}R]Ou~>אre7_]uMp4ӪlvD.3ckɣx³K 8vs9RܱK#?D<~nl) j0Gt7$ԷlBM.&lLҡԼ]j*ynP 5eYUZۍ]jV@._e.)4;]@ҏꬮCOٲ˴6x4Ot7g(]^x g.9'?. e}*i`R] :kŢ+:ٵK/K Ҡ]E.=ڥ[~e@Y`[ZxuK]Uڥڥ &ޫb(w vRŖ3:.Eۥ*ڥyڥhQ -̢Tl|W ! 9` ꎡcbҿ.5z'v%5?9tzP+`4h]jC$ C2vviџ.%Kw'#RJvFԋ8K93􇗴K.=#X1/1K#8 dS.3.%K44hMyiFp6ke^xv7}g JA ߪvVR&.Ӆv9vmJڥO4զ]J4B類إ#j^Ѵ˰% |e.5 _ؤ] bdVKA&D)"bFGvIuۥإUw7MBBL aL-+vi]~]* ] $$t]0{K39h5~4MBBLv0wLX?k.shڥ819 .F1kAy7.Àv٥YFePv(Ov)~H enY4J marK] 7.@q!^:]zO,҈R.AB)ӡɕ-s;jv5r.(i;a1bd(.2H$.R.uP AtN6رcxdw+bǎ>#X̓GQXSyMZ/X_Z*]{H5=oqq=DId pAL_i&\?D.\]4ov}f82.Ѵ{&ER$IT8eߡcFDJ!:Գs>[p%{v$!D9(tj AB)AB)A -uvMڎ] 5A||-GkcY-۴?k)й `(g6>N7Ů%OCK +V?|}ś;w1<)ص`({BO؟. jۿ:bK7o-qd$ۼqzn:NR`z9OH! k9;2a⤉DZviRDpW~_-W⋣]buc)+Ef\-[(v-:w ¾#{>k0ڻ$|e>71D~ՙmѣCF;v̜xRk2}BƷ9%^ sM0h_bߑۇ _fu$X$XWg1N5iyF}l  :^c7mHDD6ݽ{W&M5Ep,*ez*|G[IG-oT zxņoX?桇txK֖Xl'LȿenKsy#3Fp%Uc92k8wPh.1{2O7ϼRĤ 0_☃C3[y+y%1;0Z|Y$[Gb10!C-݂YK/ϓ2-\S ԻXTZt+S\`WO[QXzj4 qR`hq9nnuرhZVw=pZJ.Ux,Q4]Zz,vQ#Fy9XR6n~$b+F٥e+Ewu`b꺡HO#F^ݶՖ0&;81W_,`"k7bF#(ǵ,RkX=9/s%X uR,K~a0zvcV=T ^Jի8 rd؟y(WLe30<%滊ӟ)"m۷3jɥ} ՜sň<ƏlE\BQod9ܶr]GU+'?󌼓bɉZI뺈=#H3tEJ[/zZn9ˮ=~i{>ڝcέbUlV=X\^E}\ ؼe=zߨ=%i``0xK1ZrtPA% bnJ)؊ {cZ.) ljXWC@NnFtKy_"OC ,#{$-],}?ԓ MRJ[b+#UWAsLvNRjc8@neq'LJ -[ +Z)Uȱp.cHe71H<ggW\j8+1]}!.n:oDSᨈ@D;-U_)$zu9{`c=R> R7h,8 .<uTGMcwgQkmMV<aǢRJ[7mnqn{d;K)⯛5 r+GaQ=.PsIq Uȱs9/Ss)jgcY f_f`````L]200000d>.RMv!C$BlWB.KB!DMd:BH]B!j9JB!тvI!C$BRB$S쯬l٦YZV֥{yM:vߴݷD0au׶_,Y.:BH\K7VY"j~Z=cs$@v~S6o c+eMڂ.ݤ ъ+kkkgϝEHYp}]V!ËP1ToCF^Bv)Elk֛#c:n.0hp{v5556G,|ye.1u`mlbs}!nԵB 'j^{ խz_]]CZ/̛ɕ-g͞Q\:4BH'n߹xRaLi "G+'zi+9QIa^09K^Ci{Y;/j|9|y/߻n:X6;8%fv0J|7ޫ7K,5%fyώ_.{  +oq.4Lt%]Nk-Zd0W)K#xZ.lp]"RRy~v%G?.۱kzCa#bn >p/k={,SG ;v̜XcG=ElyͯWsb'RP:%Ԓzv)Ar!$m_Z]9Hɓf7nq ]dƂ%@HSM*~i҃T9Vɩ쯬g"VfhO79ev`TUU!n^jBH0_VU%ړO\`^~r+[+?9rThYrWtpXL-tԍK_VYӧ_ß|"Stڲu+"U0P$R\zU;v-4NscSbA "7AB)AB)!.J!J! vuP uP uH0K}RRC]C$B@$B@ R  `hP P PCԇ:H(:H(:$ڥ>AB)AB)!.J!J! vuP uP u++[i+-$yeɺu)2RRCYSK7S2hySMZ41Š]uP uP u> .lu?>xP)2RRC\0e"r)tJ!J!qv*AB)AB)!.X.9z*S e:$On:mI vyLjųf(.21Š]Ct]:H(:])Y)d&/-uL1h0KP PC'a: 3AB)!.IKP P`0JmYQK}Trv `h$Ie P PC'I:]҃c&I ?P PC'I:.:H(:$ڥ>⏴K$@z_ⳜH!.IˠH` vObt]Et] uH0K}9ɐ? Ёv,!.II` vO2t]Ht] uH0ɶKqÑ`v Ёv,!$.*Gb7AhH`q䒆d){3'lK}b ii)!:.:Ěs=;}" .o􃖥IKDu$:X]Kt] u/YbKڥ>q2X2XC|}uDmA}5oֽW*Dibg5h/"ɳK_xs#l!&2p2XCLwl?tڲu+"U0P$O,^ >K{:[>"Iˠ>YFx:xTwIKPω可Kb1eΨ \ zFm{'?g}9_Acƌ~]tQnܹͅ׾ݻw߷o.Sŋc{̢Cƍoa_̵,?)iBs5"k*L˴H=ܯ|+YG)C={"3<詧2eѣm&2ybСCwΝ;E֭[vڍ?~wy_,VUU:cL䬳~k/ ?!LyҤI͚5C Gf8|~8wO׿6[z,|pdX9q+o;\+Q@mi TwOc.W 蔖`H" E͛'z}{ DŽ&Me@igq[o%SvGeTNg^K 1'ҩSE0D}&}cǎ]xᅗ^zp]w!Ϝ9sd fQVbz}#eƍZijLw٥)|V 1.I]b[nq)ߔneApuA׍9ȀSo4 fZh?134[zzf?Q~\r9~(tw)G'}6hu#6 A3imv _P믿sdؖ)gypUWyW_mID eLsY|_k?n.8a2ù_b$ќA@Pۥ$R>?dOO@Hvٺu>mO>yov̰P&UU]ve:!2d,Rڥz7n۷_ʾ/X+Ea(঻Ds(fgߘah$@}O~)R ,0_կ~HQkť_}^Ly1V TVVbѻw>ߛ]’A| R[SK/TqpuvH|ڥ ?H(7F"-{!gIvi`s뭷):dN`=z?0~>Ӗ}Qdx衇K-<KJi=2rHˣ>7ty-.ѱc .@ٽ{38/FVx2K.cǎ}g?c:/^,28qN0aʔ)FjԨ`VuW&:txwubxs:t( <sgiFk/\IԻܹsQ 62ܼysx1c~`B]vo hD $@G\ti֭uY{/~ '|(Rs=yyy_3z-aǎ{7(~bFc7iӦYlHeMY]>.r߯ZE%5<ǎ /RAGƨ{4SVL9łܴ˔H4f9j)XqNELPS*o;eee)ҲR$ ?P&M`bɓ'1RLPbH.SNeqѣsӦM>k1;uͼV-0-1͔iNv2Y줔"VD\ƿ]_'կ^s5J45篩Ii֭N~y"éSƎ_+_9CAA,S.Sou2e>C,+/Úuh׮y-:<4Fݣb:)%ڄ:HtWZK?pD]v٫N ÇG;>Ӌ-Be,[ .SVƨku7yĉr,;SVR, T,]LY1eB$Req<vկ~)jo|~攝;wZҡC..Q.u`̜9Fdo*Я9b:Kyh,JY1^A$])tL3ԩhH$.GYnIaEEE|?ӻwo'F~a$R\`~qݻwGʱc"ߔ1ڥN>~W]uwMѯy.u*X6b:).0-{y4T?_<׫_$ٿ+{'OܣG  &wϟ]tDnݐRXX(38qEgq'L0eʔQF5j[nD{b?RVƎ~=n"ߔ1ڥN/ğ:ujNkYRb)eC)+ӜbR ,Eʗ[-[5"vJ]uxKEE]vYXŻ{;S\Z;sܫWիW}ٰa05;묳<ʲo=?:su_rՕ1ڥN݌/yof~Qf̫XRb:&]qR hPO)_w L/r3eX͛QfQQ[8:ujݻ+_oЪCvu"j)$R:%(ܰqg~P+▟v |AaÆЪC(-+ҽG& oRwIF2lK}$)q+V0D.Ӄ&0K8qO/{s.guV-rH4jŖK}$jR} f:zZfewQqTԘ3!Z ѨC$5K5XJKն Rb'ҘohLL~a77{7ᐽٻ'|nfi2C((f׶T]#FVĥ9 \rA}Ss ) <ˣtW\[GXF\0*zlu[:.^+*l< .mVݱMOqS͔e')bjR;vp*T>?rX__D\pPf/IE9HUtƥ|o[}&"Բ*B &'׬k+*<93SQ2ƒŘ+tJa\vtugb'OhYUSbٳgrqi8cWZD\`=.K@4һGګt|X&QF%Pnh.{gl"UI 4\R[f]aqi=50nyA LYtƿ$P! TR).Yg3C6/5BC'u{6˧T5yԾ%p5?VܞV4P$?H sWȄv݃RR).YgW65J nn!кUM jTAߋO=gZi/~*hσ%Bだj\ǹD:luYGs9oG7o-A-:ilyo~lE+/04N2 Q! @ZB<. *B+Ç~YV? 8).i. _9n¡Kh\h}װ-TQ>q nܼ/hC1VVX[G' ծNݯQbS%{z5?KW-T Wsc9jE޹|&u=p"4>}C'u@71g`@1*FbB5@THcBH- fRuFW]?4] >4 P*}٩_O{''Pj=xkX"蒱NDKu\Rп¡ֺͯnn |nLo HfΚT jPY풠BSR8@%A\EA\EA\EA\EA\EA\EA\EA\EA\4( endstream endobj 191 0 obj 29048 endobj 193 0 obj <> stream x}RKk0+r.LF]ۂC魏[{dL" &4[~sӤEz~P_7~.U?Wj78BRKJ5F0`%א|V..BB^.NR}TsuHpH Xh\i>wDmG 'dfaFDfiqM%=W2L^UI A`xu!]<WH!O_<6uTpfQ/?np -oun7:,9gYk endstream endobj 194 0 obj 295 endobj 196 0 obj <> stream xXK6Wx]H:e؉]8E鮽. ~3zXNm)Ҍy}:q:do~@xo|8p|:擃`T_]hqʎ;Y#mtA6?Z[S9߷:r.IYjH1,ߓ`\4x! J߻Dž;6_d#ɒ#?3}epAxgLu8ɾOі<2hcT* R]sϛC4PĤtƩ k[v=9xNO>3ϘzĢyus]hevEjFHd_Z-6" FOԤQ%܈d2ȠKy%94)vx_5i6dS:IӶMq2Uکiy۪c [pZ0la }57n8%~"|kEoUK/K endstream endobj 197 0 obj 1419 endobj 199 0 obj <> stream x}RKk0W\0> stream xYK6ϯ9 =}ma [6K~*d[ÂGKVW{wRz aןʻ_o7 pvcVJoXQVMj3l.nU-K(wu43"*zs+X3&0q:z>D$m!*f99Oϙ P=] !Jn0k:z+ Vx 0pQG$l>5[е I$MduA|xu6!OLŠx-Jgrі>_ 0oA" ΄a:TPj 6Pn;㢜՚{ţEZpNjdzLk 3﹗λU2aOd vẖ \Mt\U|9.!qVOx0Bt{YDd.X${dTG8`U|7d>F`.[Y=X0DS˵/&d|4 9s^u}Aؤ`v&r0Ē6._:a%ÈGǓp&L AYDJj1_m UFƑgkܤ #67D*OOo)"q$+9H4jL|iyYU@gߛ ٨D\ުa bY`ndQVR]@ڵuBE4KY"i:F0 ac"0|W @ƧES1=m PwY|r;sKqI!_tV:Wl YT: >W`K߅6递wv%q̭ę ٯp(#&"5(l.Rew"LW 1~ E4-5U;!*U#x!i_2@5Y\rON.t:XnܧZyup"qo0b A&a% 2t(Zf'u ժ9u27B{TFȯJ ]ٔ)7-!h!,,V@Eɺ-^d|;J%Yhj"@, l,⨟ VڲJ4=!:#$n kLDU*TP>#ƙ+o7QSJu:fOd,v3m=V  _j]2v Gpz ^5948M: #t%&r5%ԚY_"3hA^6ďB8fi׎JaZ7WJ4>y3Uuong> stream xX˪F+SUn\"C!d6TU?eKd ]]SO_a:~o}rSno\zw]ov6mܘ)Ȱڧ-Ҽ~@gDħ4lQBd輷;h,0O~)0]}ro~{|Y/ ׻j"J&B2eEmO8(;X8>*:(bpe҈_P i'֏aZƐ2&}C' 3]c#> X +8m)d4a=.X5^2VN}#522ğH -X#a6u61%t,nJI\q.[5+6-fa!yDZ[~^tV'-Q\vէt )rxfh?7mcb7ܺ%Ah S)^lT&(]qW;M((Ih,w6XMKJNR#]b!CW7CnjMip6#=a3Wc»@D'0Z ;`un/{nΌXde侫G. Y% Qo)1P1h!ga,e!6ݻԷ. 2xqM1i~4O Xh&L5)v8l;;^O>7wCY=x3-0@@k;<So|xh9&lBANRn`cOked?Ùr zctt$F*f|I53&Iy]9vz[zޮir9Kn,Kߎ[˵qckpYc*rT4|BGNōѥC9*:6g[`Xٓjs ?oќ oӽ3AVMʝVwa~Ξ*mggOzqkeY^D|KKW&騺 $ ߬L} }2EZB-+eiD Xf#&yy[@HhqA +?9%O~Ùݿ endstream endobj 206 0 obj 1278 endobj 208 0 obj <> stream xXKF Wx}!yL //t{ ]z74O;v)O2F8Ont'S~|;ep?Ο6iМXm%ܞD^ݑ;uf_fFynhO~*,ǡ^By2mTٕ&%_3Ӿa-!d$( 0$H>x7GU7:U@YU'ӊ 2?$X`lԠjcBy4g/bLJ >ƶe47ⵡtS\\]bi7hO.^̔icύJQ# Lʯ!w?ޖNS8ŹKQ5tS٘\1\@ ? 7p<)[|ms9\^C_7l1ƫ.jX6m^8`As@Յη k)t=Ijf\([3➏ZbeRmBjM-*ȳ틢bW;O%¢~lV|e5GqECZ- (fFiRat1!ȧVS aLqeh42FϭߴJRDuM"^n_ &~1%*f#)K&h4Icm45uISd>j. wk̸E?jmm s >e.=m<'EK`{$ܓYNT_M̱z(}X- )-ӗϝ~޳Bs0$&@WI|] iYr+#xg>Uu$' ˉ ]|[&ʎ9/ӻ,̯T.yem"]gOgoqmRl;FϥWck"Ʉ-gP ږOͤ)X'@Skbb+!9"?|Ef02̮8R:%X> stream x}RKk0W\0> stream xVɊ@+t],[BnYn%ںZaG^zUd? |1\7{/߼oY{ ]߇κdq磝7ijL=~4~1Nf>B ?nio)>1IFqF֝ded'FbK{7Ą#]: 2-;s\M+~PǍG/oɱ͑- Z0IhЈ/D4mh@o\w #<+^㵖y/?x[ endstream endobj 215 0 obj 894 endobj 328 0 obj <> stream x< x[WyGu8Nm -mʉ!:}PtmQb[$k+K+t.ЅҒ2+e,JamƀvA ?W[vjZͪs߯߿Xv !S m>ßG&',sFK!کxnr[c pN>rv7⾐UW7|@E)霵盾 G8k$vF\*;OCNS'4E^/݌]T07wa7+z!|໸15b]GEvwRxV<Jxe#k<{ !c})cU{i\k]!Á3p83`G={ |ʾ^d&,I8 g ygwP6_וVNc(g}ײ'-obw@a/k!r?iF[`>lyx#1*Oyv5rN6-:H﹆cVo_'~GڷJo u$ud`>EcHR#?wuԷGIӕ́.G ֌p!ۅاl-LHmXy1o<:~?E[6Ʀ{xQEGD;owZ͋6riJ+>?~]+X>s֋Y fnX{]7Rv;u3k\mw~}Pu˞r>byFݖ]wZag,n`mMw/]7릎O,}JwV\]ŗg%V'׈u֋)ћL䓝"͊2EL7℮u9IԄ:eE_ߥn-aqjQ(ƲЌ{0q5omr^43F^tw9Hyji*tuix>Q4R1ޙ׭&eZM]cz֘\)BN1*Mk"U4r"T'\Q<dcj6WfpD+]uQR1*9[T8ыIϘ"u5^TzuG -v j~Ja`Yh @I#=;%FJ#u7zd5HLiɌf$K9=o'ɢVK FʚD!I)Z)-eJ.e5Ats2[Ҥ$+m,&qIEǔHd"T'(rԚS`e:b-Cih;#Fi 9 R1 uB aAavIKHRFM*4ZFap@r1N tNW p 3f|Lwb`5zyEš*)u:B>ͩS-eRhjU54wL'T-\Z䒑<1*"UHĔHgAnO%ӻ34H$^2z%%땸'++J$W5}qJ|^I}īzi>"q%K%^#.7얉 q-?-w[&q-2ixݖIKCRЦxE鎸xuw$;u#q"ݑ ֚D)7>|GCn|Q4>˃@Mį'W]ݙ V=t-c&gXe8K3 %fWc!X/X"әr,GN\X?ʴL#׋\iyq}*~\BIUN*i$Jf fN Uz6N(<$4A\QRj[(yO |']Dmt-ޝHCGU<;xqLV'h%M"ɱ4O&M|V -@0dH2[J jbh>m8~LUF3gkOÏ*CN~^wE >ᴲDFYl "5!z9V ɔg8qɻ^~wpsb̉ e_p3` Uˍ*p,]I1;'СQЎN,딯NWEI;yNjm\IQ]8eA#4GT,z'I2V8Ⱥ"0~+6' 9$ ۓF# ,1|jS_C$JV"*M&)Tu,29:ӡX%a;r#:UDn(srfp2FnTZr |c1!;L.ǹTz )iMWF"i8j]ڭM'W۫Uy}xjRJy$;l( ѻ1>QUTk5އ60aI>Q%6#c,g>ي~<'|Nٸ גb-F J;D-?$nm'aGɢt.ÓQ]o;OH Z"ѓlw1}B'R$îbD]HR4PAQA83D:;K$pR>q1Bvh:q`uHG+k؍ٮV m$E !o_*vɓ Ή:VF&:^ΑCԀQ7>ejw4á`?Ӡ+al /@ש]aגcv]5V3XUk; ln\y[rʻNuV {;vzytNvމ^s4]A}QL&iN/=OrVyywQW-HnfkC9o.DWraf {>u}Rs]pߥ2daOvt{/DZfy}Z=U6\#[s$ONʛqS=}%̓xY%SeAބls%1gT5WL~7J}J+aΕ*_`^pjJ\oN\osnKIҦKNO7Nt/`]QeWSfNL|֔{*LELM1LK6edHu3IX;O5;sfGeygGK?;0;ZK;;*7܉?OdN| M|澳ćWM|; 5V4p#w'\u]voɦQIkj3,ϑ7*8fÑ_0/m ?_ 9 wn)? ?/3qix?RuS6І'} {ц w(Ooݡ>4ݤ|Ȇ& "}p>VŸn>φ_|n&+a{;&n|Or PBpݍp\R}-6\ZaÑݫ݆{4*\fÔ {Ps3(`ؐ! v۰W 0 IlP{` ҆fvlʎaۊ3mݰՆQ< 5+3 Wl^\aH#DmjVmjA-6l4+AdeiMM0`iOC }J `pZM.IlÑ&xqf6Ff.87AJ 7ºFe]t5BӔfX{7t(i5:`M+~}:u4*;:6kC \ B99lтgp ͙6Ra"8f2ZՆԵ,i Ɇ%d44#bA|Xx 6q_ f`6}7]a/ _"r0 endstream endobj 329 0 obj 5800 endobj 330 0 obj <> endobj 331 0 obj <> stream x]n {bKq,Ew:E~'aCƅ>k$R fїQ13.qe0HU5fߖsGcl[2opzq_#qEkJ8#e0m(> endobj 333 0 obj <> stream xVoU?wfv[ZhY;Ly}b-iwKl3؄nd_nC @b04j nI A? &1&&jxt?pgg9wϙ;0hH"g,:kN\@j$~g̏BHl@}9 8g Fx NEz4Em?EdԸ{:Bx)S)*n hkhӄ7~~gĩT2)VSfUB5x<?opB.h ~h >O opx>8ER ՝쩐:&uJht;;2oXS.yUR i^fSx$KYݍ~}=-KXۤnM:+yCm$'Pt34N%WmQGTRVJwz "`MA2!-kf 3nIYR,.X:%aƕ *H(#FRpU\iɶrfeKNə=>N0 )vUJe  &Yn%L2:n]bAA9+l0+<`!^ⷡ ] | }cX: ip\>ki!]ʝ4~U5yW/q r vާj3@52C((2?ԪPxV&ܙP#P<㴕k95ψBȉszO |cTI*&G!:-!"ޅbx؊YVRlsl,~s.⁺zq槷756]SQt}ˉxWwW'ӝL\Nz@ ![ uD%JWͲ*Wxxē+*-,;!WQmeYZ ʮ騦;\[6ZHdKC14-qa>U%/E"-7sHSnϋٻK' a/-,lgÍ{YB_D _CcF[-]Kw>kx0>xkQ^h|n䱈im:Ʊ$ΩȌX0pUy,jHZZ=DӲ`ʗp> endobj 336 0 obj <> stream x]Mj0 >ЮL;Eh8("왶Ѕzx<}YRf|pk"8 Z|UIB2;kƥS:!߸fp7BCai`=l)}!C#y2,(+u}ޏn/Qlt&cLQtM;ԅ'i-;UZrWGPVIv#T5N *T&o endstream endobj 337 0 obj <> endobj 338 0 obj <> stream x{ T[׹ Y $! hld1`xHM8رgNi,; YSrw/Mumһ^q{߸[I:>{?|&v "5F \ׂz!l=%ACH[۲}Л[R9<7p飽a(_5O1uK"ao[sBw:_#qo`oKݐ@H~`|lr y[ )B\ay' 2BRk:Ao0-)ٞpDw'-ݛ _MMt+W̨W:a!ڃ4w[wz C(EO>t}=/ec4:^Em_;ZXߗ"z݋zg:C?W&fP/ mAX>r?Q|4.,}ȿd%3Na/i"1;x6@-/ ͠ڵw;E)(uzމ6 *(7b\0.cO1U"  ŵT/ȊˑzO܃~4n@4IQ;#?9pzTemyU uښꪕ+ʗy94Oe3VR*2cQN'+FQ멯ϥyO-)荊PNT앪7 A͡ kĂX*rsZ}#᮵)FJRJ dnxB ׈Q+Fûgk{k *ezP.(UTA*3W`)A2k] HFھhdmGmij=5-T-5嫣2Iq/\=>'ͽ>gocGggٙD3xjj>jc~ot\g?C0Տo.Kg&@ٰG -LogZ=;^ FxjnchxgT 4Fk7tDIzXUzܥvsNߺ@M'~l.6C&:#fE:޹xNL/޹xVc6ʦ7 xjӛҥQ_nϬA/;"a`Dr^ <Ȭ e_ڡ yN7=lܜh/mP $B}5}D#5ESu}=jGZ;GEMQ۟x*꯭=5!жBmxIHv"BxID($ZJ iFR dR}K!m]V*sIe2HLX,#PBR*نƠk>:g{;)# P~8=+:0Qg*TJZ^/i 8[pnξY-W2-APelՂ <늋 $b__V\Ĵpʕn-pw{RE%w\y+H b, \TGf;5( #9l)5-v`g! ޫSEUx7'mșV2iaۛQOK:dlĦQ$7Wq B{PRJt)ﻚ($iZƓG+`M:P2wh9*:+J60ޗr˷{*2:o9rn:j%h*V8*ޤiOyҢ7;Vl6W$Q:a7!Pf!{ &il!Kn)tdq ` 6}r~1PA`@ ӄ0:!E(D^UJkHC)c([ K~u&ht2V&$O`n3խ1l>d4+*-:{a65ҠUj5[$֧'N0JYP#)dUߩ&^28IQ'g3:pӬC*AET*ΟQI2Cp:ӎ+w2U+FHx, T+`rAV!.=4"w(+Rq^-ӏ_|ܸdL^aǼZ6:70vfs.q>颂k][KFi nݡ>lu-,ֳPq#1A. Ȁ~JH^X%#)2. ULcpV./a'䑼w:Eh0 #,Ϗ\d z%ctc\Ib?eL`0T5̀Fc@Kp_R1 $HE1eE7QxamO5k8V[_\=Y5mWCo#c&АE ;֖n W2Gc2Wg+Ц|5-'RGdzxx5}/hJWO d[26zgU*_.+ ֥f|5-آvelgz$P ĵ^,((HܖP@bPϗl&KWR82( - PΒ20;4|si-Z޶[VCa9Tj۸*_qmO?q]P.L7踆1˛tn}a[x`GV`۝k\-O12ж5wXɱ<;WNuXДbZQO>a\ajkU)_ ɕbE"&\" bzѸ#].zE\]']M&+YE-rwpe.~}PjL_f(RO sj&\ԯ T rPm 3.KGqvRX af[[lm.>6pw0Ϝɱ?R[e2L 9K`tvlz ˆ<ڭ:P#n9!Ձ j&AN'X$܈\\HpBpQ℡4X˨v,Jq\P H`Y,E 7dK8s8ˢ~]rɗ(} 'g!?C+ 7` dKbצrNVF}vϱ5z៩T=&zmJkYKrm%I>uK,t}{5($[Gz==]֗}ˎ-OKXbZ*"=r^B2 Q'HNh^0n,I>pʌh̘"I16r&8X$|* '%![ǰA;P6p-%pnk$ f<ՀŒ(ƴ4~w-rM8!>ـmH]Kni-]}.YEe@:RhP J^ɣq 9VŬBi`|pcI,wÅp,8[Y08Aq~ )jegpg3.;3c3q zƌaL` w=^faBk="a ( tk\!1aɣ륾W?% p TgH0 l)M2(I^F"XY ѨgXCUDI ؒ/9D}6qAVh;ǫ2ӲL߈~ wMH.Iui=; 󑣳!M ¼ 'PrDd CgxaL@e_19c by$LH<ƃrcN& S HhV<Zۿ7tZQ]3CJ#XKM3&:EtZX`KCT*dD,s{tԜRIU@; RD0B-}|_e>俓O>$׫cYa}·}B} 6 &h4` QRqjjrA 4tܿ3% ~36zǀ$s 徥>À{3xjbk04l=v;2TE&%٦xkEPá'')_9"R N+_w +agey˂Վ%V|2RZQWTIǿǖP>`3U&ҤP( ((Q0 V#30$rA\l (Pg*Bq"nLX {'叝'7V|3gX niQ2 -ߟt,ȬѴDT_5]hHZ,]&qGX{| 7|ch ֚FK LJ<"v/xCo\xޟ|]jw>gXAGƳ|gtz]ϞC<@p@2x=k H!,g<3QuToQjXF߼'`tQ<×O0]-ccw?$1'ePe1n\`d_.8*fWRBg ;w1j؟Xs .??oqo~G413`7ngL EZ~w(Ut\k ޅc^fO# ](2Fs`-Fp9?up &G1x%QJ6z3M^o&2W?exz &UC"jZ:!Aa*}YQ j +3SzHZ:ZT&pn_wܝ[)=7_̹@h3B"̥PguX8l%myV<7 C9Bs8t UƓBn Y2+Эv/8 ^t$YY\No)¤0*QirxFhVѤTuE2Hr2[^|CF3PnVQmVb% Su^NFR\lIb l15\3ZܬѾ@j Zg#+ ֠SCN5(\$.T@亂yR7x.@pxJpPz#H y6̚c}_X''Gy>tz2a~p;^= o/Y[LBЙf,DfldfN>S(MT*P#rO"vNuDL )|=Lvl<|a=/ ﴯ='ͩڹT⩅k0BBf 4a0Jkfr:mhlƸ۶?m Nt z78]^g .x6lH0#]r>F}~u^r+UnIJ)z27;FZZJ6n>rt*Γ[^ͱrV60og߰7^P-olv$1 `x#JGkBEx*P*pǰ64[߶;+cfX*Gzz8"Hw&E\Hz*ŗQ⎗ͽwnɏ쩻{5]g.h& y:W;gu?zgd[_w9mgpݕP(q[G"ʆdPK6L3 G*TBJ5V\rIR`D3ͱeJ+%FM_7K(i!g٢IE= z==v.sO|\|_+= ?ɨ.=ց3?1>р`A &Ts7 ..RqB%u0X|c*H7xf2eáWÝvgrᚢ+Tb:ޓDiO>xo>xg>Mv @{~~VV&%OynX  =&ϋEVF-fՁ/3QQPe; i#S`Fqp2$IDLER ʐҝ \(ԇK'4ZF%e zuj߷{3L򡆚ly6ksprI͐=O^tl;d9,,ڿkS p@qY B_*M\]a1I*5kig7f7UM%[օtu/nTr}~qg׏%f)K.US$ 7Tկ{Qbzzz4-[{FGnd9z%#!#CȳW{Ha=eAzK=OA}_>oK5:IF%S4ʨo8|zbB0א:e b0-R| @RPpc`lRR2X*ŝKߣX?H1y/.X)9 }&9՗^yLPenx tC=z.4]ƃ5Ւj  1 d\SnZ.}HE/ *}RzH[ͨdvOt~uѨh*EI@˻Sq+ d]WW WʛK _?fq{7b No쪛l?зRMrߺHM'/ LeS*zj"} BuxKVٗuU-S'}2̽wjq#)5*_9^_ _i׀fCk^nc1&?+,[ }M|ҫ2H$BX $MߖV-2 ivS?d-s8=ƒr~1/SnKƂZHk6c9d>hRQ Ld΂YoA/wh\-LEd$g/yM+eVըxwy[pSlw0֩݃꾩ɱᩩe~={n^wojȖQԎM0I.GR:Fa䈓"m~Om3moodtF3D?gOjqM A,|K ՂAljF@WKS{Rn-+G{oԹDyRj}n $h\㠴[n|6wz6%(4*Ю%sǰtKڡ+KO&?$8ǀ-G` G t|tŶ$ʾ:ű<}3>E(Amk>:4?d> gk8Iȟ+b_{5|]vm5q5}2~e SyW0_ׯΌ>:y Z]/=uE s uXx/F^~1";=! ._{y b tgu?_y/z+3Ȑ&#st΅r82Ky`-h.gq~EGn:ՇŇ7 {]N~K4}^ Z߫օu塚=t/HNuݺ®SSdSN')B'm)ax"pi箱q;:?΄ ưp`*XRb`Ub%xA0q2(\U:z5~d9{ˇ i`IX9|X=TmM78pߘxww‘mIA[,ȴ@.}W/#|8؆>/p\@M]|vX`h'ʗ^1':X+ɡ2h%bw][Ck3oGpsSk 4K5M1\ C]xtaAZLܮǺv@N0.Rף;cu:Y7;@UB53&m`wB[8'[hi*"h4Ck(jq U9^Ggct!`AUSS|I䛜ÓhR;RTNx S]ٗ(ҽ)$' 'o oūA @_Si҇6M;4kT&Q&m=; endstream endobj 339 0 obj 13098 endobj 340 0 obj <> endobj 341 0 obj <> stream x]͎0<|mBFfEL"5J]$lν|.; c{ 9Cm6SCft};/+o͔zfӳOS]p1O?Ǵ>ާWa6EVצ s3}i!תC9 |<`Dז*؅Դ!6%df0t=+ &MѢX:KSV`pIv3=_*ߘ>[f;fvwfgJl o?-W/qFK/q^K KҿT7{<_Mp'BQB +W?__3_ចN{ҿYdҿD-ke0Fi1I6e endstream endobj 342 0 obj <> endobj 343 0 obj <> stream xݼ t[W(3hαѲ#Ke˲dcSbv$OGI&i:6mP  $BFI[.rɅ25PHNz_dsӷyoyan$1C%3_6!eB/`!Lˎ{!5sρyL&'!']Xvcj̿y߮ 7Z̿gzxϾB:-{o ,!&0/;_7|o'D"ƒ*b0yxZ F f-Vq%OWXT/) CHnM}Z&Vr@_ 巖wZFk&b"I y&&"$yxAB2( OE[0.,d/9Oy||>瀁 9.)#i" ($ '=,۲R * &ӹ@6rgQo/S8 ܿ^1C$3ErZ|8&˔wQ=8jPQ캑e&]5,Ca}L7=#u 0c-Ï3 f$-aF 9% LRd&]&1Xƥd~p(ۥ >w4NlBoc Iw%&Zޔ.Wr5R XvQgGHmDkYMjx\]<8`cɿV 8nxVIY>+u'yFgy8?Ƴ .[DH` I$V ; Ѣ`Z@{%ipC<Ж!vFxhHۚXHSh>  [GH7/ ̎UGQ8;_3x=ES(f4*) @Rt) lS,.AP B`2 'f.1"f Yȉh0jbhDb±FuM J\9؀‘0_H^"d7UEficdFlVd_i5|wwhkZu76^~]v.//167LK]a5_VWo)P+\,1-gz #Oi@c55]uں:VPJեX>ZmZ-fVhm-nc[9mf5Nx^r*+ah{ZnX H- 7ad8,aфUY4ړH=2z7Sğ͂#G%邕<m.!žYUZ(K!_ĪxxKȘ^=j93pIZ7臍C_ȪT,wM5/}I,,t]ۼ ʽ~62os<3/LhZ~ _6 Ͳ)q0\{.C֟@rrZBRg 95b:V bQB A< ҋM!H\L̊%f/V7ނrFQd%F>{[uuOhL8'\ծ׺kx]cԻO~vC=>^ZZ[n:5,vT^CiԎs r]|]R B[!t C 6xדR??W̐b8^׋zFW>ͳj vQs\sZjlaf0=dM^h6 ùzHC9vvnnR~uu,,x6'HD)|[PT\RdU!t@Iqig_ Uɻ:ߚڊzf.vXumi7m0U#mYw`s61TvG|*oI%yv9ҳ{1_^۾޼F}nW Fs7v|-qdzKXͶ,`Ȳٺ^ocEq=ݫ: ztlV!7Z 1,6A@B:H ﬨx1""u"țY5ozI0 vEʉYU+V]DQ5=&\~Pxồ~o&Tjࠋ1}3ү-uAn#r̼@,\ IB3!?a:g`bC&SĚ`@(O!,pXĴ$rrKsRC$}̦Gϛؕ5WX8H 8oWfkzg辅T*[/GFmH-Ono NvDXNaK0Xu`e]I*7LaUUU{kUVUur-Xn,v>1Ęcc^Y,1ˢEen?~d," 3*K/G|EMBqEQX|c#-Ŧ?wĵ4_C/|nOQ'2ٜ`o<8RG~l$bmɘbm _&.2%7ZPeT QYJdTqf9b1| ks2ZH.bH5;7i}.iԯtmƪ]c<Ѷ6+dw;{m{6UO1,̓ߤW_֐ȁ~@Ō%\u$LMLkd8#5/%CqQ 5xpMl<@a 5͚p_K0ZY&Ah2PRd2A:w[x{}ܚ]ymy.9-y>_A^KsyϫL/+yR3jsCWHŁĹ)mqéx)Oi-BH okNi=8jvEHstP­E!BN 8K8_^y][XK,ڄkHZLrs]4)m]d 鬉1 H03ph6֟?! 7|LZKVKocCO2> xlBFQ9(E泊ҼMq(p Wl˚Ub&EyGREpO^kik|ͦ|M{:bׄ71`20et&Ay[ߑ@uUgi9bʶ5)joYnl[^3^1U_rw-F+Ҙ|[>с^݂N>&1\L:tH*VE7x-"ǸE8i"5Nam\;ўЪF|/pSRe?+=|{ -1ւ(duA4Ԃ65yӲ }h H vnuJ-R(- Jr)+],= N,P Yyl ;T.,XKKV $q%v P$)ʮdea`[^^(4jEܿ_e$FKB ,2n7Q:y1l5*dBB3LCm02,J'8Q j3sl6l bJ )aroPi y><Sal$fъ9*1=PS?faRhG8L=+ r:!**dWj#>0Dd,kLԜ#b6d4M$F7qxjҏ bQg삐+&Pi!*Hwk C01^:lOZQh}bȶԏ!a38,Onc(Id,Qd#csv.3l^nQ+0'sqPɞP<0tFy:%IYt-JɪZz\ >f&3G2! 7S_ؙ{=P:} ̩0uZ"zo-Ƿ~xlXzy/vpMegКKf( jAÂΉ%>wnɱ@%!0ůotБ+۰{q$Z5aoп^}B׵lo-Dj)?%<~b:^c u׆ZEѻ'F?<u9rQpmY \CboD_MI`A }.ɢAz՘b \p{}}͹=*֢1 j+ϡy`ԓ1ܥ6n J7)щVBwьc7=L3j^˂Uk,W195}r)6z׵l uƑ<$>>ϡ~ò>l\}VE Ҳ XO9-պÜFyQ8&ϊD^4wba cf+sfH92;Hh4=Y$ObjvDR$xs*zTD4@9*wtr:3cT6gph~]$?o6i#Ȗh%F|R4e0:47hc:!ʣ*5áfisoFvsQxɓɰs 1ewbt% J+L 2/U̿WśPUmF[f'-M uܡ?3q'%7k_Zͩ4<{'~׮n feyRf5]Z]RQ#`p}A<3,>ȱ=637RH=ǃv*"ITBp< ϰtXS@′ 4ѭP" ie8//FĿ5*-TAAV~, h7RRߤ~vΠCH:FgEi*=lJt_ʳv4`4 Iq*F%I>L2p.&0e`<@ r"֖SBXxhj,obciegivLh,PqBF0܉=aeZ)$f+UWa MO.}=LUx/k}Pʃ"XLf"&yYWG]VO<͍옛wR-pH8%pn:99 9NӐc"k>mhDC2\t߰X ME\Wiofpt9+IIGF_s'+(wkE6mwVt|8>X~uس#=yK>w^Z xPbXpzNѪNXϫ45Ej-SS3%lA52m:֢Q *JHfjIV*VE=fY%dF-t+ ^SkYƢ9`NaT mҍ zN"eVoH w$i^|? p?-}S[Jե^dJK#K>ƲsrG9h%58dRvw>Ive<"*O=T^Q s[ѧ+%OvU-rN)qOׁЂzS0wX0^?6?k/g^? ?|}m~ϠOp֡PCD֛;syK9B 4//&ڣ 7ymG[VW&qҾهwG"foiws_w/Ll&1YU7WTl>xĖ-k,݃ZF| zN&@S ;;93oԃMr$Ǫu N^!Nvu(xUv.P6 %҆Ga/͐^&S l,5ƍG&6>ûg>7l&7mlE۔mL=.J9b&XUPWZֳn@<q[FI\ RYlY&My_7gU95Ysz _ #„ڍtEtؗ׍ }PCSGnIٵoqjzM8q&hQv۷o9 MjQyb5=3זk5;7ozdSIhx ujwĊ"%YRmO0b[o9JSn v#F@)·RPPmNCÇD'wĝcN,ʞש3CT4Sb$}^ arCN(J ^vzgFfzwLZ)حh}I6uCKm^2 w_GK:׽{F_zp3|k꠿gz}ewubozP6H:C53{,caYs4,2 3hVRidC{:eKjF~-O6_Sax"u[ӛZKA?h;MGvDR0 é0̄!9 pfsi+v9Ŭ0̰? d^ _z2Lmݲ|{: 'R ]sSӔO,t;ez:Cmd_^v]e^{UȀyLw/*"!yQ8<~ןoĆ LDéd K2س|n-9R#ȗEG~̱8Xvp؈ZV\8FyN٫9>h4큾F-!c &06 vPzss+R\%qdeX1u4/$D @_/KJ?Pޭ6WVΊjOة}'7^P^:1bKiظsl/gqf[jM^@A\u |I?N6w(ު,k:w _ oNqFs;H$*y,#JMuJwKH䱫KAr\Pb="P#P#|<wG`8J f")"Nތw"L"CvT# G _,J8oG|.UZBD2Eక?FB~"tnUE`@Z3+T}-/*Gex wF`Ni9=Ip7CzOCp 1եq9JOF JS +)9LĶM:ʋf0\Ieep D/t5p"Pg#p5hbt˟Ja?ͭk)L|FuB@`K,R&ѕ+J5[9HM:5xrX(,!U{*pOE7};eHM`+ʕUk fgptXw~.̆j*7v[lcU- }kՕa{w{˝VDFY7 Q?B$9%]dfN'&Z`.ҠBGm՞,.=&zrR{//wy~4DQoSFԋ*;t' ?2<d jd A0]QQ8(([VhPVHa!ggM'{|vN>v.|7'{:kxSUz3{xתCG{?SOG^V>VwNZtaWgo__^hڀ5=Q;yPUYh"l&dLvvb"eewCDss.3SN8CN9N9I`::fNK?6*:>𼬸7#/K;'QNG#}F \`מ[ƪ_652XX]=2O.̷RQveM/6 (axbcݹ_ ]QNtnS%.-fbRGzȸܠ-kU [#NwbioӴ9ґ#I~3\S)qGXMV~mR\rhVZWsJP'zu|mbÖac<0P[| #k%G2rMsQvHxG8gb/qp+kZz"Ň9*T>^/hR&WΟuF0aYD06}iFٸ͜-8UzLd\ ]|37Suehwo(<=5LaM'T(eiI̻9s/#_ZeQ}+5T/ SsAϴ;tqRãF.SA-oĈxcV$o-ُX,}6qjB!ח5yf gִzOnFqOS*X Ԡws'ә4Kȍ4G\T&F&B=LZ?Ϥ5[2i-1L&1rG'2i#2iP :,?Ve 4K٩L#S4Orf*ԙˤ53i-UMI-?IKLg&rPsS;w-H%R$&'2sryI&'箛(;[:cyi\Z;>w4S&I[LH {7o719'UPզɹyZ^Q)^pA c _©}`y_ܷ .U1}R)>90 p*MOLm͗_atfp MJ= v-,̬ v->7-ڹqPkanоy~Gjއ' S&ONJylmfnz鹝맮 ۛڷ3tJ(~ 9@Iv"?NJ!aTD,2e$SdIZWruJ] ٍZH;L0#tJkv˦џ{wcmJ?÷ۇ`~Rߎcz k5Xg *{-IZ+p&$-z gxMN4 B+Pq&>jza;NܮMnyӻ2Wj!ic_CJsHWa:|ALn!kP_9\LJj/B[@>Q8NMA^\ )C1Ӹ[NyoЕOZwePIcmӈIJNeSSZ=>b;3eWfe,WfߊBW1!s$j4B4pr-sK . "ȿ7ߐZ/Xyaڟl-;3`jփ|ۙ'&ό->y޻>sߏ^x4`g{z};ΨcPeUXB^؃q06qs\sۯ16u]ɶmح]Ȳ{H=[@n6fG̃<\fA`X0rlܟ_lǺ^7匕ĘT9'pXVrXL|#Ia{[Q^ֻ˝Љ,Yu0 A1" ?Ww9!k)2Kr]3.-4 ¢ BH qἰ,ӥ84G gc@r_WRI]~z{7'Uw%摡1Ғו%򆻒i&ļlex~a~aB L,+0O+w Qʰp>S>@[M+Yl [W^,(_ ~B77 endstream endobj 344 0 obj 15103 endobj 345 0 obj <> endobj 346 0 obj <> stream x]Mo@=vv1d!9v,vj@V!0<3ɷa<ǰs?ts .Y1].Ik3ey=>nK^gS_.p1O?|Oӯp bM\]|/X/1#lҎ]MMfl]YuwTr:?9ژZ,ʫwS߁KR"{ s4J W-k+qy'k|O~l _`W-fc_GKR/o_i-+J/z>~7D; f._s/OFBRs/ZKOCB^BOzN2f?rL'ķ9^\?}q{1Lx\WGE\t˱?e> endobj 348 0 obj <> stream xռ xS׵0>H:ٖdymG, lcl$ !%@(d"I3@{4i $&!m &inoz MhޔBGt}{G>a^{Z{n'1do}G!z}s>Ofx''D-ݰec7+&޾g>2EQ: oWcsg ltBgƆޕG{pYۇz7Ym_&D7MG|p4b0bqJXh7[ĤdkJjHwfdfe ݞ"[\RZV^QYU]37{ܺzC#%!A ;lQ7\|%1]{KFyBd/geL!? !rzhȆ\{G.k+%xM^%8J?! I Zt#=Oc&p#`Try~ru0%aWɣN&yi^Dcy'cyRA0#GȷCk/ ;4'O}Ż~_i`xrR!S'JrG::!WmqkK kl͝3W;.,p;lfѠiEZ%_CY=!>X^L.'d$0!{f҇k@f h&z=f>]_\f]T 0Db1p` {}@==:wLۯ-'Ǵ: 0qYhN}1J4Yji: :%UPTsCj}Nُ{q9zWt^,߻ww :B\HĖu!ڴxkUBH4:{ ݘMQe ,Gw{{NLX{GN,51}֐e!cTF_o]~@/_QnuMôl@v O}> #'>kY9c:gx{soϜG Xi 1gW)vj^ߠ=$d![p"{JD+2+wD6$"{A~ΐhDov`}!s8dvΙOFV`[R$Z,d"=B:V~oO]yx+[bKsq\e[XpwZ!2eel!rculj,`qvZ#hpȅ4{'rЈ v?sRgj6"ÕT6TT;JP. 7 p8MŢgnձ )fۣc cjt(Q/٘w:˜Gr ϣ}~C:f!#!z=sC J|&ygS^EHy!†dUf?N/Nb|{csyM۽y}{m 4Jm[X]q 0s w]m]py3gٱ Bv(WOQ4i@VOHq:bI)i^J@uޞel 9,s1?'sa,6bj AA-{/ تII>\դwq5~Q>~Y}$8,jjq`^Ô09=/dI@Mۮ:y.~+t_ί_UUJ`E*%g+}_E^(w6/)G}yō<5SEqq=!?9qboWn\/niU "Co\]H_axȭa,DO4ClpE<#",N)/70T'wE3EЉ}b EDODfUtDqAE8(a:}(|R? 8s9#UxEgExEGtUeXdO|DU . A\b$r<iBgjJz@<#9"E8,jڋ2t ^ק)7&̤L'0ŽFG/ϕDxL#٤$rr8j4ssùBA0-ȐlF )Icl$^ rWla]ӣ1-~ zcQtvOx%5WAct Oh;ȻwT.;rnnSGQ휄ܱ>wDq#8Iln =Ù4Al0ښY#H!uC^#**qݨEQY, )pȊƽ0R?ǐbkFn!1)88_"rي,7nd['dޘ@kJ@!a۹ UY˪lp+u#=I"NTݨvc6+]vGC5Hh6"V&hԡA]G"vNL8f ݉͢tn 3P1'Pxwn_Ffo~b`H,}:C:M$ HTh0Ld564O\7@ !=h D t #ƷFP-*!.h3,LF05|*$6Wwv΅m{vo  nApΪ6߱8^U,Bm6} *HH@kQciDѨB"(ѭ 8V֝MTt[Q8G9`qIn 6lb=Rn⟩”%HW3>8> φr_iޣEgoa=qoҊ}װh:I6㳉ه&4ٓ=4djmeX`9f ?};2qGvJ"~iO[h kҊu%Tbnm7xG<y֢[{,~ǃٿedԷvPmɚV>{O^w%̒ϡnn_f5N9B=)ghpg7'XVR+s0GCG8OrY8iX8PVZ5wC#O+ m_/cdX"U9hѱ[>-}lÆg<swuw4k+mL /r-\s̞2w521%ype=e6Xk)3noG+_kqz^]+wnَ  _M(:9=kZtKbLc:3%%e1'@:{Ni 2CyIqp ߻,rӺ:4moG#>M%+OCb0SDŶvFm ؎ڸTY,U$%&Ψ6(kJXKygʂ5%*(Z5v.j[g;Eg* *E~-8PB$jcX_,']v p@8'L %A&"Ak|ˤH$iqD=|wI#Dy! X^Tb?x^؛i{O}^T2ԜFS@(A:\Tr:A3uh't(}#4;r,ñZ5w(YLceYPݸM55,EM)ʞj8 O>"|7S7ՅU|+2^Tx O1?eŶ 4:OHlB4jWջU*xꅪ* HVWAOՎ*I1~tX^i3WՒ_ )eHcL 0c%yd W  1eޔ_ŏldU@ 2zo -cs#nyhQE/$S$gfAp9g .:`r+ɉMgmͺwf݂y*?80Ql`^w&Bg^W^Fӷ=a.W*T%{i=CS\nY7o9ow]9PZOV"!O;8DlU*Y|⾱S=3ge &snlg^&y̹.)0l\q+.|8_@Xli-s4RVL#>:W  xI'?dL2OW`LwS|:[endVYF3sSvJ *l^ Ʀ@|@U%G8qEVG+c&!MD5{zfM}Z/+Φ#v#]j{B "4UBVBVK3(J"eIcyڽvY7ޛ; q3!ee_CC'+[JHWq$XQVqpK J4_uqsqվrϺO ;֕WTRe/qN9(? tqG<-TytKS$gda>ΈgѰʚij&mEir fdl$ 1.QQԜ9nSdjv);USfRl@(Ⱥ+L' so\` qbs/Ol ^myZn@n gc̃MOPu 3c[T냆T[*M=0K0LT֘,*Ծ2?,\.| БH!Ye^=.&ua:א|i>5$T\hv:-^rCDS_:Ua @i5Ԣ{Lg=!u?&{y69}LW!$>\ެ MHsZc4GNSXXϳjXBd)xSwm(83|B> 4d*Wu5nבBdB9PB|y6!3CQ6\zAFE͂(YwͦH,Ŋ`Z!3-iEGoY@u %)$hmҶk-. .o?断o=.6zriboޚN9^>r8AcW*tDgјC-:0l8$dj=T ۓ!5W=}Q[4lo:Ecj`u!iAY,iF`m8K@q8K3D cjK­{mKi*GykYpIʼU+{ׄ?[f]t ؽMIM}kk?25U.]i^}oz] g1ڪ'`_Ɠa_WF38ڱgiXa [<#^^q#hV|x?4L"ƲMXWD q) ᨆ 37Gx^YT|M~~Vg՗W~WJ漸+CiȠ/Gv.' ;0jv6 XTxX"Ig.BN:BNaŗnIЗMDdBP'CcHXJHBtwc2сJK-dZm4uo{֡]$i 틓A 'ZC#IF cdcd;}&rz<8?N۠p9.HT]<`{;p56}TVYb MaCTa{DR8A.;*FTܸVEAI꾄[r+dYwBIw,aVW-\Ek8`"`LjL:[-;6<d6%%q@ƹ z)22T.s:|(ׁByk.6Slwc+;60/&xA6[E[}<J1h5;Uߵ(1˓ʻ|%Y\G3KnyFYlKC6xXR! LCF6(+ sK) O0&ùKɴ%B&N]8|1r5 v H{#\i(15]eL#g)so:/p} ?Gv~p}eEieuǶ7@7,-͹hkg~R3pr3dqeB<532jC1o3ԆpVX\).D*±OMHԿG̚e~<0!I5=_br{rswUs/Ҵ姵36)`!>jt3,jҨ٨esLhDz,˺wJ_@q772U3h"PLY7{rI@Ƌw F57jgX/ n'>ɰsr'>ݫ(ۏ_HvEjwHHEbq:h PCmkX:>|H/WnBݍaJQZT[C3yC.$3~y 6Kcc)]$Ϟɣyʩgc36D$$r!1љ+sډ,wWħ4R{ =Qn'k~;z׶m#};]wvTQx/a|A9!oݨȼ RNvR vR)lǰE)r 1lzKzIoDSQ)+/WRr VZv^0 H[wmԙH=el3AY_,vVsi]Kaju-|}pЯ5s-Ir c{:x##'`CPM >Hc<>z*?f יbnS Np̙;Խ(AdVv.8d,3XYnڐO ڤ1U'.F4Ta%BoXuw46c;?< ˪Z,gQE"9g4, OΝd=BGԵ%^n_[޾wUImek+cxmIM:A\W9lh@c^^_syNJϊC*vrn~Z_lMe7Ue{wgܶ4t;c9ٷ|ophKm6T.wYYUN)z(__3!%ag" *O}Ep:AO6tx27Fc<N9W逸AGKa6tC8f1WF9W{&梞 0Pݜ{bH'btZ֪gۆΫ7.- =3]7m>eHX]q (kq)%~r3K1$cAGwcVIR@7n5>]MeZׯآs[QS ûa:፫߇ ؎>K"Mfj-+NQc=b=m]\RMhdYWqQ\g*|=exqRd|sgr#W\-;wɮ\ywȘZ^Jy\ ),?`٠?0Iml%Pm?Կe5"_+>*yNᅐ2:/=}hKxi^~ApO}zR/zzk}?Z؎5G8(f34p$■׈Pt4!fL`NIn_ܵO?~_|ʧ *e$ߧ*rxyXHkD$Ck?B8O\&r*^XD~bsYI|$U%e*Ⱦep؇" =#PO3!..+~K"r~EYRdo͍W"XـDxR| ,`'>bih,6|dQӟ fOx# Lmd g\RYvH (/6=h(PF\~DVJ%4Op;~P늜e"02n9캶b跷QۃW6 &i/G*R弝l/&=&.^L\p170/0/lNJzCvF0^wㆽ1StѽL [1cs PS>[ CWfg'αt4egʵ]G) ɉConpmw# }^ : %'v8{0[!vm64D,^DřGGH?FVn7x }8v%iyxT'0x K|wD|r۹Tc^h=TO=EUA1 yPU1RX~]qfۆ w-Mx '~U9F4 tZĺf؊eM~ azoovhP_kk㒻R?1b5^tciQӗq-q4fY-s,7[KtuE &&&>ieIJN5FOO5u"M<[%etaݢ&Kfg0R< Fh'fro4,~d4eD4&DbѰH 됆3Gñ$ߋdMa@rvE@Ҹh W sd狆y ${2V,GѰ|} kHhX$) H`c 3%NDzr޹-k9sSf_goϷZ]ha]n+/7~y͋c^ho_/\?;> n=^`Je%c,SV RCbx(&; -Cޡ>{L5kW+G{88>d4:876V8Ӛ`cJn*F7elpp`|HIv҆L}Џ!5}Hhp]ZM|Ck0,zJaA qb'9d5Zv\O=+bOHz"CBbh6ـ?uƔX?Y) `9&n&̋>=нO6{Ǵ YO_W)A6 !9߀%gcx5B )G@矕ϔ D(B PCX`WpG Ѧ@(%ƕچSc3ָ3^\xsQn#1tư6&GqT-Ff΅J2Xހ&Mʯanļ:P mDq +|W{-FP{sACC<`t]#GcЯ#3i xXϲ7+;MX5J= 3|W]U8}8c=6Uji=_Eul>;KP< NK`]_.ܗ_KtҹK\}x>5s5' x=[ilv}xsS!͗%7e#նwb{gY}mB863};)Տokc |xY6 ҳ9 8@NON>)gO^>)Lۈp)OdmGGk>^ o߻=%[l |$'y0| ORm{lcx?G!G%6}lޓe;pl4?o|p7==kwl;vt3 q0p0|a:0 ˆƇFې_%CbG7C:T;X' ^h[϶-/qpu90p -'kKKG|qk9ւwRsn3]> endobj 351 0 obj <> stream x]M0 1qVvC?l'EjrȿyV!у=y0KmcXs?ts )\!36vIO^)+bq[0&+ǹ2?n endstream endobj 352 0 obj <> endobj 353 0 obj <> stream xԼ{`Tյ?̙39L&df2yd;Lr$L@@HH@O%Q KT "(JPDZhZr[^ZoRZoU>3i7ɜgu^gYvAꭷ-ّ]?CXnݸG2hBرwm;Bl妥/g흂>uiE]}V՗hDr=B>smx76B\r&n5OѻT {V-mI_ju1N޵KzKk}hP1x )$-VݑLq= _fVvNn^?P ) E%e#USjjF52}Y45Eoo-Ȏ6i>\] }z CGKځ_w=I/g⡽{i>GiW?4vz| } RWS?@o~sV&z=E)D/A:_уx6ZK{NZЍPNt棅46Y7d:+~ (;i>P7ZsO쇸)ϡ-ButGe&E- lh6,Z_mgd 0ǺeXuQj+!ĢNE5Z |6+تӢ=l6euj8T[JvX`yfcy\/QjaĎ)t\et-vwL[iqyV%lʻ@j%)M- s| ZXv\V Z\lg=-EZ)L#,>lfo fã毞*>b.b)ވcԺѺJamdi#tq}rJW}l#%K i ASb6deuDt_p WDcKxIRP>@DD1% AGApI)&DG#w\c؁Gcqqfn@ ,:^ _,5p5p} X#0 hr?Ai%^⃰AH|gホ%#iĤ4 lC]6s8w@utMGٴ{UlSߔgę.Vwq*d%hz$r&LH5!YP=z'M06L03dҗ4T}:V5k׆_,,Lߞrq5d_%b_Fv/>sQi0\E…XDlpar.9 pNu%W.6gf0~e]í8ܾ.Q*F[y]N2_$ኔwtZY}/W:̴tf 7Rsebv7]wO2Rt z̵:,@~k.Ξ#Fo1gW4;r%j^i ʘ:")V52M O|٭;g0%Q\go^ 1ܯT7݃:QGބp+$0pI}~8Ne%y g,7bh%?2~a$gl5Θm3.5n7)7;b;jdcj `Uן8JFs%DL p|$}!_JpF%*핎JJ JpR:i]'=).FHQ_yLt+hQ"4lD4,"NRjƄ+6B.\[n$]q/M} c]~Jβ 7uWT%r 2$$9DNH0 IG$^N $P%(CE{4Ò="f5J l a:`aKR ;k|v]_Mx+(;:5NamY8_Ea*B2ZraxN;~[Fx!`;:Z8qt><1/5œqK\u6lK}򠌗d,ȣhK}69iTUG*ؚ-#9QXVZ%ł-Һdףd*ʫ/)r۩x]dn?b! b*8.܄u׾,w̬(N?蚉޾;oow޹cU􉭕[/(\.KL#=,zҋhSd~i$Uh ,Ϥj"~.tawa}*VNw BQKS6Pk35p#aL 13yB実&4Ư/,FeDF5\T9f#\XRjD3%*." $=~tֽOԊyEMUٺW ezČ)`2ѥ.9_&Va;gcr )gJجhqGzzW鬒J}Ջr`.louڮY趗Qә1U6"8h 39r.pgrT̚D|E ()Z]x5eIbPbbtMkZ<.2oV^fRT\.=V)U{i*hkdNFE啶-itbkO'gMj>)|o8Ԑq6a㘑s|geʙJLN9՛ \c9M+i~62qf/ 0 &jB][]PU)^w7th WVAzߦj0Uܸu qzt< gx|^~&K5R̤-^mlc'+U.]8CQ:+[|XoPQ!֑=iY~C9c4?'?Z]r9X|^e0Nchd4]AC]&̌c?4c㓁 Ay:'de n944P.'8{Cݤe~¯R+*5+'ۚʼY4Yr$eBujί]:}w}i 4&\tѼB z|-'7%T+ET՝CrTJlkJE^ϣƬ,D|d.ɳ뚄_m&!q91m\~m#K-[v:C}6Up{/̯A:<-ٗLDhZɳӃקnMxeKx2s l]"6.*964U2)s@ɄLm|&8jik4qE:j1.{_kvjVvrGz',~2yFLM3=0:#I*3#L fFJ4GzVF 6 񪹳SUICj^-z E#ӧW#-#M8p{%(s19 dT%}'Cj ] &Q!h .Յ1LI̦#G+8]l?R_F"U"$yvE@?|y3W)b-!2?"¹#">cL7GK6F1:#@9Ʋ93+"xf*#Үdy4De ;cD]g#Dz^0fVh%͟Vkgo1^+UJh>g< BC+"P=d@+49XH8VM;ʈYt EDD8ZPj>1WòP i4ՇԊ뒯3I\y4C Ɣt :CU"(*-_0U\ְHߓJ'6vo%q !ctOe7tKi m؞i8C(;n6ԃ9[T0@O<$&KƓ2YvZڌF̊Y5wG:5E!gsss:Qrš=jF)"ՔFG;&4qh*}=hit|{[F+wL#/[J[8垕3OՅEu|F;7 ~u^sƒ VSˡp΀EmvrNpgSgl)Lu@[;{ڊɝm`d\Ҷ֜DhRS2%;6IpN 39rQmH VC0I&{  S:a`wZ(i{ j̚-2f'cӓ04pve#& v>Îʥ<)Kn8+zҽ}K:N;&,=v MQ݉w&\ %f$8|'Yxi|\=b]>ebL4MLzr4)K@];y> lz<@f/٣',#;p0} , l=\ZkŨ(PO x2[q)8? zh](6xm2-pNz^#f @SYʒhuH&#q u*PRKJ59ZI"eGg>pZ6@7cG鏵HWpIkXSm p( ^`% !GL&_n9Z@ǔpH]38쇮0X߸wܰuȰ 7L1P-_6"PhZs8;#AѬEI*(^z(VFDj^֝kLz`N@l4bě4œVG)2|nJ >dccyK85mn[q7Ƶ2ަF Q,CKrrB,낋sIT6WdDԁL,jcfkH569*|*֟{t~pUڏVud=+0=H'kQl[1oGaĺQ9_~f{,~ $G0W ffYD3ѽvh<ioWpPA%Fh@f]kpٸc6`H +>s iG$+Aہ/HÚ%h|<X.MVsR%JKCWüQ KJH.pH\sR*UlҌ>{F+S5/y-pDaIcsS5E:AǽH/Q)cF2f# #h"FӸHL<_ vc4ypd-5B@8'$nfH*oo̩z{ܥ@s(Yt>v]͒*f;!IkmmN5 v]MF|a{oq4qsFt'>=l`T.9mבymBdaa c^3"m&z^#a?7#Ě[0DY.qsKfiLLR,Y%uh4 >y6@b\lGD'Q 8G=TT̵c-syyjF'=ӱbZЯ9x(n=q>h=rfˋ4ٯ@axZ܎!\af!&RLj 6N#wRʱaVEDd/˘m [x>+'?=A/AJ#q>6Z\`oe9hyE?f,ZZpj,MgYusDAg@,~M~ 73^,Z+XWz- j 2DhE`M"ļ4NǍbzw?"fKf{ Vy5 [xɯGo?j3tV]idjzr$6 P2.D~O`A5a a( d(HN`Iyw <QhCi^?64 }ŰE &|TɞI['}I#q hEѾ 0;%UМ$UTlRBp &96%Ltii81Tf(9|"N:vf%Fy~TS R,>Џsq#|;aol>7s q])SH[nOnLe RDYhUZm3Yپj¸`h3˓MH^uB$ /T4s܀g"zCXoO:~42Jg7;Yn}m{FO}~fWv\YY}DZ՟lVIˣ9嵅?:Wzzg7vylmG1205Q#83oR{P]y0dyPef>Ixݘe$:ȗ:hR%Ģ?/K/>#^B&{g{q<^^܋5ң^Qvy{F-N7  40@o&UԔaV^o{s^_^ӕ NqgtS:F^Akxg\ +{@{yͦFޥ4CA7) 16Kn-H`"PSltz4)f+ g.cFF;9g>4ɘ$GWWrg\mmQ8qv:2|اo+i % ,;E^hrKͰ<|d[ހ'o ?f8m n68;LrE cZJ\f%Bqqbύ~\˂)/ͯՓ6ֿt%~ѿY~O!`xeWGx|󹽍{ms:zVW^}W]vm,Oi$y@[im%)6+Nm@s`} P6GdU sva L. <8uÌBtdViך%*r.!iM.ikMW5s©jR sչp?mo7XԶr.Jyu??nq?j͏uٳ A`/ýCQW~ Vs pLzÐx2Q5T3 2~b!iglt/1=1_jrG9u4<-#U'#.W [͠xIG%*U ma!-7H$ܗ5WqJةBs {Us9Y5aZ!a+52`\؆Os}J|6,^}g61ddlߺ b9[f i`tA-zǓYӟkQ306Fc{پ 54'qYuB?mkm /]^5r۽?𢱱֍Ϭ gDWh{vl[pw;e &gn:r^~ͽ}T.1j*+[zYۣOqOVS6C2+y`Nn:R*{Lsv-}n+æ}1Um>Ǡ n#=&܅q˷G22gt ^@!J)NMِ9Rf}:b>mlPǖ!-Si?`vP?@1) G +`SW\ۛਲ਼r3OEjyece3?kM_yhuhO݄Y܆ɓE᝞㛧.<pUHQgUvr 8 mR-3(hc}ԭقQZg4n:0Wt1UowNN$wM(`Ӆȇ "%`;Bň|zVv99l4 } Wp,'AXAIG Ԩ')bd|R~]L700=l:e%Tu̙5ݮW~N$%G:Je7VWg+ (I ~ J CyXyJ9p *eJB S96Pu3y? f=PθPsY伆0vx́d fo`mf sӐ %_|8(Mh2t&S @NN3}ٜDV?hDC1FC;՚ 26nKImvhKKL$mR4( 4pޙ;::fNZpƊI3f*`b-}T5( lgQAh0HxՊ@߱ & Sst$f`]>\~^o94:Q!\9# =Y0lOnN&s`xl!} 5;P՚Ӡ_%a_C= <@C 02 #OȳbTxxFr(fݾb_y PJtW|rPZL9ށZL_e;#9MC{u_ooh]?yw:ZP:zd4E79dlSwچ?U %uυ`~A/ךte]àZ%mfocaDOlEm-U7Vaa}J*P辢EE|cu>C1%vt0[DfɒMNJfڌS5Sٵ@d^+Ork{ )^;/G-Ċ[\k물κYCgÁI&ퟳx,_o5Nڲq-~ֺȩJ&eM\;,!VuY7{S7X8+T=$bMk`~ S ȉԺ?$ReR ./W2o6QM I i\ vaa*/9#~8X__}f_X o{޺'cL(h|r:`x `'v9WQ~a@ zXsc0f7 Lj[w8SXÖ |4G0[LکTdsqS 6H5ό,^{}t6~t?otW1T&'/-r;0>ŕ"."~a XgRBH#W[[1 a5lp ޅ׃zt ѵ4Gk`er/G>l_c=an)' }Tvp=0\ xykW,,2\ p |VclA v>Ԉ/f#^ޠ69\tNr ѵ1!c' 87++5Gx1[Sʔ NOpG[KNOGS_| .+xOExS|_R$P/ĝ"W\w"B"EŏE'EȣtKN1O4Uf?iPNiq`a _%>+"~"~- M"E|TJLv)[4,)vX$²w"faG.pX<&xWT휞[43Q@"~&€.DZFB9s"aHfˢGTF <2A/mXBbpDfo*Ԩ@'lO2n\zBJ<}ŽN\q+w>ko>ײ&׎h4kCs V1Tvqލ%!鐄FaaH֜/\:3W(9=l^k`B_ tۦVr1?^ 늗gK#\{Pˈ−\zA1/ߩa^1duu{tDИ"WͭsrW9)widm'h]_$m#>G^\˓NINyQ_ؖؓHjfn Gvf1{ vz֣vfP"N1*LM'MT$7#ePNyH>#_yWcтKer. |&L6QȦ3{K慴-ؘe7];I#?~Ą43l}rS(䂶?[htMLNÖctaAG#c3ZΝt^bt^,tBrr( eǮ9nOv9:f8_:zy0+Gn<ܘrь6LFGǜ:Ϝ0<<$ΙxGK"n5M>\a&pN`9;IɞgDjqY9@vr <ˎR~)%R/8y&QtԾ{?~Mz\W%c fB;.r,y^͘*eJR cmU=3 8~l)Aq?K J͡%!rOu" NIN;vuϸ9|N*(j"` 2 [S6T6\5/3e0 ʎaSY +Bw̲1Q'^y ;ws5ώY4 <.͙ƗH̿9e٬SsZ5Sg/9aő4WdQ{ /RCu!g$]/9S p#yZydy8kӚ,<ڂ؁&Ip-6h`NN4GyvuaL1/xƵ_|PԔθgm=pg߲ssvcۮ?5) #5^(/~*`l0rjO/0v #n*ܱu2b^k9q8~"%yc2>citWl N*[sWdkREq"mw7I۸Lf\';H Aj~c>s6k5:EgG#ёơ|Mؔe"e2nO*ԠN";Z V ۮif[k:ĵ]l cG!vAx1ZeGg(& DNҼ)Ug`x5h(7`հ_#4Li jDI(ˆ (8j} 0װԀ'3"3Zg 𓆓`ڑL84.Y1 ^l-A3'i' g d0dFCòSܵGEuzmWT H?/& /h$cL|D($ø; I6qhDx$l6dvd39IM299fb/%4>3E q0],& z7?`6L!ܘ†hz}[{6 ;wM!-܆‹|l}!XC 7+cO!a⡅cph\8q;)3!# iÅ&0,L_.aH@ 13 Kz?2~HµRItGH#Xߕ>5 HH$(=$H8VJ$ ~`ݶeJ<"XN cnG]QyP/L #,qCZiDIt3N.HKu")Ck%$eKJPOaA2)4ŽSkpT.gL 6<ٹP CI%wk"5!2p ޻fzÈ{a +`̙[PGȆڌF>EL8Qbu8?>qCb\@#7> }X|?? Tz7?_%McxclwDPo MY?ރw /${t>ٙ  wvѼ -| OR\1r&SvpV9;buLaq D%z@TJzi,Y{L17SO%?Os*y)[¨Tӂ90`d2=''+\qqB\g.8H5SB`f79*_^;b6ك rB{M>gaՓLg;͇ A#DG'=mzvbH6Sb[.PGJb}`*% xj6}ņM掭t7{Z͈=>2[_195n'!fW)}Ks *7{h]lx$re)%pWXa a3H KumvUEMED,+_TZ$- T8HF`<WGLӎwԙӃ=&gU"DS5zGityIZwPuT\M'LkĬX*QvzD%MKZj2\VreC|sS!s Ď<@C~yw0xͨ[l5  ';5dꕷ]t^cq ,ѿʜx ]e,y.|Aa6-&+c욧~7w?Ccϼxe_\Gi{:յٍ-=ML?zi]ɰdEd.*j_%Oj{ۊ#?:E{jCukJq&v&RԋxaыDN”}'WYܶ,*?-@5b t"A_̔A_b<Q{1ˣ D:e'.Ar0J!g'QX&6EZ!6 $:,1 2DF[,P8`>2M.1IaF^$Qr$ D,aJ|!dE6@ʏn}{K]}JO00Gò??֗ǿ{5;dy\{#.yQۦXl1{\pe352 f3^lk_NrPД -E,--7͑،B_L͘'\hr>udS=P3#ↅ3ofyVr>F¯5mK;:0ϸ[d_ cg }6._Lr_ڻo% h'9ܗ@ W[m8ph8ὐ-7Q|9IcwCe(͵LÌ3 ҏ Q3s<~|HV w0iA.|@ 199q|m- 9'saAއz@:̊bI(E`ۍ[ H݆#jHX^X!4E֘zL_E|l61gtDtO wڑX%@ #w}5mFČy3%<y| %*ԒYn-M;McvT JZE\[-Ƕ6lp95+99=wD*̾' XG ~5M',z.# 4EQN ӯ"uI6yCxNKȌu:tX9HVu:S:mF ,D$:Q254Af:O)ʣE:-_E4>a(Si t8*NHtUi3f/ twhrf9ȗY'[lrM2gPW6yiyIiuqmyr٧Ͳlnnnnm_l|\W]j  wAN#ߖ{3ֿhS5o"}~Vc5_rTʶ6_GxZ30sCWЧZXimJ%`g@Hv{e͚UM,۷njk֙[l4NoWm󦥠_ŻxͭMl ̓UWfUm: ۷6!y>&E/ c}uQP;@C?U%eA1r.PhɷHPy nq.\%Z+rqfpp7m?6B\}oK!}~?@rYv1y1~^FV'Sy:XPw~_KڼKѸˮUsVxi~U%VBmM.[pHr=7@[; Z'ݽm<ԭ+֮ݩͤ.lUg;W0yf忇#cFcTW,]i;{\W,kQbm=5*d맟O;Kׯ]N ݉֏]]ô#:ag}g/_Yh /^ɴNUÝýÔ中g]g+n?{쩳3O:C-gpKx%ly Y^p0"CC#CyI2rIb?:A?G_>N*#cc8& >fzW$O&QQ(#}xr|#t{zx޹iTU ~|tXwWnb1Mni]c]:Dȡu-E芉DY1Ht_Rjp[z@fSn|=&nW-uQ9:QFj,b[*-KebtAܨR7xʪ6NTW =C5WYfȰgխY4Vڿ$U ԬjJZ]1 ˆ^ Nǣ՚uϭ,Ue; >ł9TH6US!u!5KհA,ZŐ@z?ZH Я endstream endobj 354 0 obj 22174 endobj 355 0 obj <> endobj 356 0 obj <> stream x]͎@<atdYkɇ(<5IvU4=_ζݡ440opSSt}RHvs$[j5s'uچs=~!CR/1T J36MU~NBd%XRE*Vjv/޴XNiZ-bqy A.-x~g| g\]# iqUC߁ i9 _,N&7;R=ΕM y% ɯM/wW'B~ow@~B~|򫱑_KqJ~E%,%x䷞Ṓ_MG~i~0[%/q׎#\GOzFუC/ѓcO~Ō;܅[mG7(FN>Mg8#7+4 endstream endobj 357 0 obj <> endobj 358 0 obj <> stream x{ tTǕhzuZ{KݒZ Zik$@;H! -H,@[& ^H&ء*/|OI&!1@fbon`urh䩒w[~aqL;&lw1}BS30!V>cdML4ϳ6_`TҔ\T5Z`1-8[|BbRLqgdfesr=޼_aQqɊҲ*X]C?"XjyÇ/#fr !dſ߄B!=UWm4y_^2@bKCKO@}C'#Z/Lc}8 dXIH _Dɟ N"OC9ctt7 ⟰?|3ȅ8 *~vB!,A̒G;&w^: yܘUF^  9FN{oC B@ ,69V7E_]a2 o# ;G"oݠ!4d\@l#_&[Ƀy$9 uPC,+{—q}$N0=]m-Wlnj 6jkEUeEyY銒<'7'3#=-ՕǙNV)rsHN+0tW0ҮA\1r`V:!ǀTqcMkTST8]u.]Jh1tb G}X#P`\@wZuՎrsij2]S*"44% -6ĥZۺΞܜƐU'Zː6$t39f5<; b9~nneBY]Ùr\u!7뵹8ׇfp9>"8?ޘ3͑>",@\ލ.5wZG nl5 a` ʢ 7LmC4-] NcR "SMylDho[w$ !R]*|l iîz63R !ݿ'8]s1FGG@!!т7@NaM RB (ua7zW@k,;p愂;CbF(OyhN"_ ]5薚D̵!20mױsuX_oBG³>RHzXek-Uz\h>0+mѝ =HWHc4PE)Q@";>n\ nB4&p=XрF\5  p)jMeRm#c:;z$SLp8#ŎB\*P`n,8n׈5ln =ȐpU eB4'/%2Cwrdƥbǜ1:wE;DM"i "-Ӣ[s9WGwT% bH34w09mE8ͳP5=S% )\`=cB!OxI$dTKRzhX24O#yx ~prXGJ, FNЫ9FbK>#b1XyN%_{r5p,|x|0JT"l\8wCѯy^% l(rZb??D\Ywh<&#fz sU i UlzUb]/#A}/#EIuiH T(`Zf1[}U^u?޷;w+fq(s[QWŞm۪zKh7h?zݯ|iwA7F~ 5UdZeVU[v\ѬT *P %(gTfRHo9Y҄̍Tt DsK#B :n$KXڷ߽=gp cSv(Qj |aw}Ox>& .le"Y>lE+a0vUPYDAD8>JkY3}WZpHl3Uqk,_T91@/io)Ybk6תP|;DbfS{*tRn\)nOS609M wKl$٢! "0%>c8zEFii:=\K_9:׽rqV<>ao/[9;c}2\[)qx7=awLH3)0[|!p2 vdݴ4w! T٬ƶTMe#ܗ^/_.",,c$pF8$gEwܪgGb!^i1 3_v3)Ϩl3Ï+vvkb=ckKI@0όC9,#GxaDeDix9ML2o"x^9(Q ~٤L&#^}Jq4oߓqLF(2s)5RFp?5JIڢa#$+ ]$5d(hV-g(c}XcASAǶHno[mͨjJ4%b˖"oGbm,ReǎShʖC잃C/9څkvhI!L@G*IPyBOV4$ *[`9祡j+BPLCwh9gΜg|y {«^F-6Cq~ ]KlD$6$2%/oMwX$2f1F쾌ĸ }XHKieVUĩFӱG'`on 4-:6mCT}a8YY\^^EtVLlKON߰kOR˔ɯw)Z{o>ܲ`x-o!dicq:ւ!?,C 0 zR΄VU(lU"BI%sȯid"dfDqeVU$ *1*9>s3ߗQH2y6`qyqk\p-:R{ ϧy@͸AVv1OM𺽜Oi#!6!žP̰`6j+~ƶDC53^n}($ׄ O4%"#VZzKǙ:.Exĩ/XKRqBm_O&W#ٖyD%Knեd_RR`ѹsu ;x_;5"";.S$vՖj4o,3eЎBRDf&2 2 4"3y#. M֞=pIr"gy efD o3YR"0p'xwV֥[Q":IhI  WzIcQ,TZƮ!oRȻ [j" G)h(|Z-ݳtˡ}L.]MKtTG6.!||61KVJIckb9d`VViX* JuS;;XokT6س4;;`hiMjf'JB=9uٰxqr njd.0aI6=dGb4[<7kwtZɮ(L7&V|cmSY͛*^&OHVӲz}vpk\g _Wdͻ?Hq={Jc 7h xmjhOj9mQ[f-&kLz6bf2ɇSR)͐N-:BsDsߒ.H C>RSlY2[A{ƸWX-_9}phe|~ϝ^:ʓ YE+ aocA|## H/}i=.~+RH$H.Z(0h'h1ܬ\.&fOfey0xR ȘGԋ M:,~Q+ZZmƟ')Sw/2m'@Zx51AdnϞ>gg՛w2QS˙L \P]s@#/d>IJxfbϽq; AwD J_$=w9 br#g-"ړ5˒"3 4DdVK +vpODa/4Tm[XM]- $fBSJEg4uHp)xu޼4BLѺݳ&^>olgSIJ'DwI$զ W&'Q~ m rZJT(XHb 1ic +z(4SH'`vkulXh' P$'C/  eŴdcB%&c;nuBd0gI?s~JD)$ 78BDEiJbֺ֜,UBb*K,̵ĄE:-V:9F?G1ف<޻guamSmZiI'P&vw&1g<OLL^ӴVUs3 4!!.S"BoED<9u)]+}ը V3н}W[j+u |:<72monNf^7\ۭhe6+H^3XoW۪hg=8G E#A0Pͣrmh0FFM\}nɾpłzK7 ȓQh  } kZ9rF+p/TB.0%fq!3lhrK[2T@:rGܐ[8~u <'Tov '5{R2[>V—m\s7ngo3xw>&u~~PovJ!i~#B=I v(WE%Yғ8X!NͫLD oV@l|`'yY $3)#|>9i\<g9d!˓8V<w!:8,.NfTN3F10^9/f/h@H 63Q4%g'gmjEKRPkXI&o۽:rXA9ƘXkfȾdy`8ɹL,}CWX`Nb~%O^7j(K ( pW ,<uGa" GïB$_Ji?H4y!l%!raV GILtmP[H;A, yNÞl!/B^!kŲI+5 T=)8 W.ZIzI)J$NZp/}v勬_T8|ԶeixPA[ Tv||-i!2oygOlBt49 {87XaJ&Ө'N;`QkfV&D"Fo T,G|蓷ׅѼ- uܷ1뇏C .3i!Wɭ8ˁK: t(@ <{ %l{EYbrbO@y͒N؉s(rKD_N&> z' @ByZ5ֆ|ܾ%S-i(I܈Z"{ICqX׆5 `Yp{>9QgW㽵٥g"!*oT$l2Jd5JJLH9B sq$K" T B0ȬVɀA)#?K }QiYX'' ^ /kB:|{`oѭƒ":1~+ˉ(ft PpJ","jw^,(%|z0$ ?^Qq+u,2={]Gf"WcjH;&M )G` ݀St:bQ0@/.vɂ닉p\ԂX~^p()G_|}xz+ULrdPCSU؜{RhwzI9$z5Qʸ&Gc+6B|X%'+ "Ui5Y-fA"V5h&y p(!UPp:SĊ~疤#J[%m47b"8vY=m*E`??XSw'}iwg|6kE}ց_Z[T8m#@u ޲Mj+KmH[5x*x9"UITUDQX+[X@|?pklӨ7SHB?^xP~R"{ӌ~0xk`T68Kff!Sbsuh]Jg uv8QYG:01 11bО;JQ!f8M0*QDO0F)#awf$c6bk2hc|ObJq1A¤0wRyE<%U~y G % |rϥKL&/v#PZ`VA %{<6sLse~9g]"JN\8`d1"1;Z`xïus`}藿H~|<-ԯOx'mI9Y7*e 9]*ѡ|2PZ)QubZz.j$B<x*' S #+Vҩ>2E{1ho`,:?A>iig[!-FXwЯO#!v0pe]=lݶ[m6M ]{54܈4&S!4HK(Ճ[zoR:)lŰ(|1_L/Z..=2 ,7b 2ԪyS-ejuudù0rɘe¥ )bGqv'v!KW|1O(dA) RsJn%;M8Yԯ4+AI%cn)*&8T =Gv>%M=&zN|Ȓ6-*IY8fE3H<\[SeI#6A[f>rh`C&6xe!/zK2/-(ttMf}"9A* n~Owk [vN21S&$g)&'QH2bCc,^Bf_%yHF#$ңaWCkz=O{t?N'8/ܿ!QJI+`43{235Q5mPC4Yg5a4{%)*+ЏKi.dYStHOO5^v A0- _ hd/ܽl3<zN !z @ CusoC_!)يT轢5]IZ7mhj@ ??Qڷ6lɬ+Mk9[7=rҺ6wIr?;IuVvN! $Et! Txk|,.eƑX:9|o<ˀse;̡d_)}ꖍwr?Х:*6w/m@t-G-RODEHqbF0;6hjhbi7Wbdgeg% 97yB OFU-ْ՟u>Bbv̀z}{T9DҶo_:yuCbkM"_j 7?M}fe=[akIyGQ8>8W s?ɚ0Dg0N"2>n_#Q'ysH7x9 Ql}-jvsp`E랃1`:N?~Ed*q*'"n gwq^v-O_z~ `aCk0oU>9畃Z c2T 8(Xpl;1Ю'>;!ƒ偨/FlIz#"76H7F`SVO )jTvӨ\~ } WR!pjǰ IfDHG\)5"]4fQes6\hgÃh>A.|듰d0+ cDޮz h@X4i`3 T%@@KN ZrMWM LX (׫4 UE]]fD5L7َ4kl2'-rHH1W! S%ĊRya ͭ)+8qN]SN1Q#ɣɴ8i4 gV˕01:\"czaBa D>h0hgpqnEnEnOD.KWT%n"'Nۦm 7ׅ~[VDܻs}`ࠡ {[z>wt81[CHlrhEߊH lIdULY$պŢq$Mړǩ'!U5V$6~gz!e.3,\̀gs²M;Z j)svqn)"m65neꣵrF2quEQPOT{M Ndu*`je8+?lۯ5߲}V\nk;= PsMdž6̇@?M(^6&h.O/>>εiʨȴTz|Ӗ'vϝ(ؔwdE 2 Ѽ]/E1MT> Tv6ʫhB.ƪ*UG#zpJ%K1`UZʻ+^j22#W$ b %E/H[dnBfT T9sgk+)PujggVj+ P\ȎgK+5NS|ήm`gÎvזn,lx`ꂁ2 *옢<3iWjI›婩ym)i--AɣbR,K,LĔD:b:ibj7I{&:{VO&U*.r,"ȥjDKؙnf 0,\e HuvܙJHl((sV7m&6yL)kĆ=*MmܱzLXiͶCͶu@nL^xWYW^屵uWuP:^UҹYwx-گR(UTWS5\=!"INץN2z6BZ5r5N[Ɣᵓ?މcp+XAAbWU*ս9I f-+ZH&9^QIx_с}_ß_N?gI{X,}:~~sz1zx0Gwp ȆtFA~Q}s\Ӆ ?կ /h=?7j||?fW{DɞXxx]:#_"PGֳ~&#d"{AnRt2=XV?!sJvTaV6bGA&QSĒ9GKv -;0Jvmw ${T@2?tz'=W=ν(~,K='/S))W(7*qտS>F=33']upfdb181ְett|hD13'g;wOѦ=fS;cj2CȮǪɉ2[n F+a]6U6{jdxdz|36mJhb),``r5R'1=2`Oc#Ԏ#C3oF %:Yk\˓d 2N6A?' u $%kG 4 ʶD҈nx0VMױi)5|ڲ+U vlӉnэK1`AIn&9JlQc ,]%'qImǛ#7,u\ Zzk:j#| >z-6IH}GjK}waVԒhFmB)#؞az!LGzXۛQ -mGa<r0R~{߂[Gj%L]}]x/f/TJ;Kn~x߯7x]S\`|>Qy®? gKt&=z5ޮ쫇__~)Tbp)D5泠 g ձH(ig?I 0p DDђ=]a&֧էyq1/-$Ѣ$?9'>?QxAc8d+=OKL#{9wa8LacqG`~D >g1P'J)|rߔhS`!CTGGGg8zlbĤ=a?+@~U0"ƍ9}Le?ߍMwMgwN3[cXq!gb08[b!/c '1L C\%q]2䞍A [mAEy!joh}]o7o7t u\<(Dgîj֚l_H xЌ-Al ӎֈ[Ժ@S ~q6Bc@m[4&>0X ]K]}ˮz^[ E܏yW-ZA@9v7ۛCu!8J`O7$;"]O|3PAGwh 94EbĐtJjzggv̸3i|{FʚYDKiDX̙陙;w`X1cMui"Q Pf)Vzb45?Q endstream endobj 359 0 obj 13614 endobj 360 0 obj <> endobj 361 0 obj <> stream x]͎0=Ot13#E2Dʢ?j@I@,Rpmvɷݡ448s׷S /]Ygڮ+niqП*˿gyzM;,:qy=}kgSdi9טCw9 WqzmIi6ƺS_b*Y,BӹYOԦҢ:e,k{ 9@8!0ȯ׼,[7u̯; {޷)ۂ#4/~A?owX_a-Kzl~Z+]~~z0?d6Gap{Z~=:IW_X+@B/a5b--dŏk }GG3^ץ/ %k ᾧߡ/6!h!4}R[#:q1J? endstream endobj 362 0 obj <> endobj 363 0 obj <> stream xzixu=3lCC\@@P$A\HJDK"@ْR{Mdkb5h7l',i~ym^][qY8MD` @j|_x̹{g^ r8NrqB̡@7!;@̑$v/B];T_"+"Du6 ayBNǣo|sȣn+/˱uP$L?֏f|(zңXz o`B_\J&_BbJ|Abe\TV EF|ۙ 'J.Y#1 XLm(PZ&=W:yxj('F~L#oWW`@Tɛ'Y#CpjyP@?@<3C ,`:C-X٩;\H~JD$mYF>IAEޣ:O4fVyz2r<>H,H}n%%ަFI' '#CΞP-ominjlQ_Wq6b.2r:U 9#) A!4ŧʧRr!(" RX4ɧ`BGׂSoUwqYYR:TPu(5b)R`bpTBTE:$)yGJ!ġN='&Ģ{Stۮ\{RLU"y!J"vgzLQ#a!<{|Gv 0nʰAK)mJ~2H!! 73)PV4f~Ld,X)#SG&7XD>&˄%s9)c (*چqP,DDž0!@d\(IYaH2j5B1 VoDaBvӵMuIb =R̅,C#Iф;rM,ђ?/ϋn&քf#ľH#j f\/prc}b݊u/$,%bEXłi JBRLBHs@$rdq8 q . T42c~N̯MM6N P"(u(yNJ-S."ޟE- Q}~]GZKŘQ̐ĹBVtɻ.gh~$봈fDs wρZrm\K'ŧhz;e 7iSnM T 4))'8:= CMPT!Q\tu%sVUNl4]4[QSH|ӞQ&}|y K sbrٹK'&6'U 6yJշBmCT)V- rAo;lc-z:*/[59ԢLJ(Nܼ}uh~탰@[v?2=R=Q;P) fG<l9hsC:ٹU*Hl=*VMc9OP99*C$ #ǯ 01 0a#*a"tĸ/y(9EB_V9Ѫ\3vW'mޢ{{EA=Dm >z2jU+Ο?O?^u_=* yb޸aucKm0- GY{c4PNX5hZN5k`@_-L0jP)gMN}Kh0LN\ȕ&(H0}FZJA[tŀ|ނ|^Ek?B*rJLڑw w9WWLw ;;j59@→ʠ(w6~%fFPe`,:mHKk@%GO 4>ՊV(lrUsjPaHA p@[@i /b*9&4|]BNsP =yL梜V zGZ7Q>o]"S5upm_?Uwd ݺ=<0Yd8OJ_`3a"4G\eAcG5ylUsNkmmuaZ/+c}Ͷ|ֲ7({S~b!(5S :B]\PPR*U0 Ye95VEʲN]ԪWW'}!8a18o} e֑{rGԲZJƇ8PAGlOwR_=\۬5 {Z!Qk_5Y|;( ۩1z-7S U Ԡ|a}AC]\ V\Z]⭬ )NO//~@x ]JDz"r9 U$~]>q0{sy(ш`÷}hE%ࣷs_.سd׳zA 42Ѵ4JEyx SmUZN | -&}&UaVCZY}0~`ՕT(qwzt@[ڑ,i).+]}<2z@Ǘ_PX4;67|JlN ;g1[Չ=[?uWϼ'w&?\/n8Iw=0P dLbTUZRjZG򸪈o28ಕl:˖fG=9(}kG>=T_W&Z;ky#C:w?6ܰUzf>v߮1o5>i߽6/t`ƾg$ q~gpUJ炓*pkzVc^R^z댢\βA W)@pzIL@Ц.5LNfn,*vҋSH'[Tʲ?7vpe㱅Zl9RkKD;; y8b4[&4P66buz.g{0mSoqFb4W@P.(*)TJPr(SrFAH΢&&Pfs\uXhbfXwF UJ3K(r;/AVȰFϦP>>45Q#I˜Wn$heATE/>ix1Qtmp=5IA5LB(6L_+ߕ '3/̧jCOT\n n[Qab95pwSnyy#x|2OaO1_t&3V>%6g]}]\b#ާ-7nA~Xo)߬ ;MyCívM}o>5d=߂'ګOs5zNY]s%JoZC\F;$~T PN XDUF\Jr-le V+0q,d]9pq~n9ZoROk0lAvήI>dqr.mV&>hMfQS 7'i!ҋ飂~:D\!"SZ]4~3qǮ: E)jeLRq煻H.n+6zrC Zv ;pp=Uf欻Ȭ5' a8\^CƢBw!J (ѡ)qyä[Q>}u|('_Ͼ}d%`SF\BY:  Ŗ8rY㕇:[E-eԲ_AzLvsJv Lk9䤬Nj\ 2-b.]ova!kVnZʟl l)EeVn%:j7{"BQa-h./Oe"*9;6ӻpf{-h)^5o-d4DصgS4 q0jJ7YÐJ YJ+n#ՓhgFl#O+o#ݧgu׃RCuu]/Ab߃nGqPP^^^WN!]aWQCu^ӽcut_:]nrS³KqN[v r]\ݢgӫo8"cŘeKYLt!^+W+ Xʤ9(jy*'&._gsX7#w̯8 =:|=yVZOkqFӧ:Ee-1X :xrD1s]ǭ\s0u4$MݗQ-RBǺ9iЛeQoiWзҵ>fX+Ⱦ$36 Sٜ2Y[~3 |]<@= P>xeOX"6jevWx&"=-V0; ۻS1WxoldW'hy'0*'ずN3Ȍ#%[%jP+>g3V8a+كyi yrVRw~zqLK6n=)bWGUy-co߷_kb}nWҌi9GW^UI0nwJ&qU+d?@Dq,tūwNj7LGdݲS<ʜQܡSVl/FNgfL83pٍO"Ny l d[$imYX~" 3DK^rb$ ~; +faRfq wl 5$da-AA@@,Lz8 Ӥ^2RG1 3yL=YCV ƑUD=JYY9d/,!oʙ,%G?H,ݺ07+f*y]c|w4Y,8y`_WcN=863/Q>EW.LWɅ"ߗXL'dLL|1_ D5TjW|eU{z*HᏌ'8 rau;H4_L?p`vva&.!g+('8,fV[3H,%2cNƏh2_M,'K.7fg98Cu["98DDH131%K4G8.g"JډA8nslc59KR❡\x"ŰDZLJ-JT#8+7~ [KsVn"%ZEJVPurFwDO'%iEn>NLS!]lIc\f)/!iJ> sWJ̗~c5 ?w;#oO}mߺ_}_+Qz}6 p3#Iԍ7y=zr~z(ހ^4#_:%J:EN7r/ufC8ë9j4F+>9.QhTGH[ykz{ y_|pg?? f7o˼3!#e/cYr?}Uu;'d3wsWHoօ9Loh.F9nԆu~~1Lst t:q1C p8 spJٓ)۰ NO=Ms4i/ 㩩҉p*@@#j] $'fʫDU{HNEltUvuDvvE.)6Y]BD'E&E?dH endstream endobj 364 0 obj 8157 endobj 365 0 obj <> endobj 366 0 obj <> stream x]Mn0FtMDBH) {H Ypz<=oaY_j=,᫝d ,J`p mrlMfkOyonn^vg5u/V>ƍ1_0^XSлuZ܎}{Wx_ 0ǜT`6y,"͉J^~EFQ_rY\Sk3b| D3g 8A.='%9"_-B(> endobj 368 0 obj <> stream xԼ xǹ0r~UD^zQM-/3>Dv)yBn"dvwO%Ar,5C\%Ěm%Hr+|Ŷo x>r_8=JO}X!QfT)%Xwb쯃O4k=o߉㯐۔ISa]8甼}3(>O}xs/لG7[y< + !ȝt t?,\meˊƆ˖/.YTp_Y1 {\vd4%F-@rk] >{o7ftpcV0.}=?) YH0+Ie^xs}4aEJz泔=x *PW3iJzVY3V˭0x)HrBU=!*(} w3]]{]׍eǹDwDމ\Z<[O*aB4y;tsTK"ykscnwin|)L۰Թ;&j\=a ❭mHlZ6A3kݛ11i{ Ɂ4xXzkjݻz3DVO.̓̓]^m|f]i|{bl=6^ӄC;`vW+nlU]O{BB`SXqrc0v r/axj5]vD˝bC6!/„ X o2|AŻpvJ P߲0/§pG2ܱbMۋ&4hi{Z |Yɥ,e7aF;^ SJr%O=d93Qcyp웑(k=l|nYyk5qbE&[V= Tz{NHޅ,?#|3 yM5y:'#DX$O V>#WCO_U>QL9-gDgsYDSfaߞXĿɌAh%䀘)DK~]WnC.zV--=7}ENBڇWC{ծR|rI $f=jYb >mބSbbbCS}*|LK I B Z1ᡖ5ƨ: Z3%* ۶H$B[8`  *ۇbY&wn  VX@*"0VAib_N^U*bڤ}ɩw rڳ?n} PHw$0钫ASK:I=Jy;@-Y\ )S.p$RRib5F$SnJ"W#WSWa֕?`9oz>-VqP7@/I4UQ Ҷ3uj`>ht|o`]yF^ʪ^[R0H􉢾/W }qs,+vròm5wn7F=ewa<>rI*9#nTUus)y<࢒)j4ZqxF^ZDQ?IW5%Qۤ9`S9kj6W#9PnJ(`:}!ͧ5b6`D2ULу)`"8(Ѿp9k/:3h4 2يSAA^o C3ADZY$5BJ*4;49,kQG$$//S.(f-`+\y|C(?(\}Czx5 e1BLqN־c{Z)[`,֫-;gɚg75ok|Xxc곞؜xűbZ7BNy5< Qh \pQALRADٚ')*dL# ڇ;ڇR"w#5ŻsSwEq߂je&h҆& ߳Y(>7$ O@3Abƒ0$8Ĺ$94KM@h/,hS u@6E<&FxO9ZEA 4-$9):Nzb0MxteL6%YzC6GbL` 1E6Nc=׍xbWf!|'s#^qFsAVWBnyec( F .EPA@gC=/ȉVT5W]XaVV[Vrr` }*EDc&stҧ(R@&hh"/WPS1EԨ#krf;bul7M.bz-Юv`"3e mb>:nϼxSrW'hl)vMVcQ'({:vWl\PQVb;ZLV{vRQI6 n=/d΢ڜs*\kt=L@ZY"kMrMqA||<y#gD3f-kk;ٱV ¬mĻR+yPy.Ylھg n(\Y6=pm; %A7 S[̓ uY5TB+7l)NoYj̮VmZ|Wkk%3a;]=?Zٶ) ڑA2&etA搆fJlTp&9֐b:E# +J@]+ܼ]Wkb֤)1e JQ&j, +UYZŭ Œ`DΌ@XPfP; ?.[S[x4pwZrC;9gxIaMkUFireQaM-yUu |iҀ9e%i5l{'M@m$vu:'p]1QRCa4XP\11p=Qm|gF34Q ;l6E+kWq. AN O5-C͛9H1rNDY+#a ?3 Y+A4. oK (~ד ϳCO @K1H'[)Jo-M E]Ih}%\ڜ:?]+?_>AmD۠崲& ?3L&jLŕP\)0]5)ʤ\viǔ9T*4.s[^gN[j6_cu]_X|ƅ/Zwo*nɪ,Q Wmq_[QIѰ<픛GZSƼR'1`qo̖Z5}_s>dm*my #^ 0A%d"THN,\HLH5)r^[$I?}DDtY%٘.ka">#ے 0f2\f hF1sGs՚d볉1GlG-nh+>ϗm}‡8p3`z$ZX7xFKT/eVm&̚ܥwHE[_j m!/H\QaP0_y>G " p&kZT-5ՄMNKl5,IIo$~U TbFv ^Fy@T1ƴj@bz\v˙ {>^Ci&+z[bzduIUOS/)tS:sGJEQ& TFSS EyIy}:=s]+2?їZuHWE3˸|x8y4/#Y*(P MxhBA_hWBǫZ!jaURUjj]5N5Y ,TU `b2M`dh2fH4&0d4-0bjV5<_ Tñj8P Xz=!T `Z WjxV×h5WlUZ+M'`[a6TՐP |5|8*T`QZ-VځP[ GЍ \BШ`TR~-3jQW3)`= R} a%3P5#j{GHox޻3K:gDPo_5:!\w?]_sX ϝq26 Pn^DE]3GkDaM͕Εi]w܋VEIut `o4On D3QкM_P|7ZB,*iSb+?{Vo؂"Ups?)~ 7,j-Z ΑJ`5b0#o {N^-Z+TPP&3>%-;(jԩF+uF^dŽ?:/'_LXi50ru/_ Z.s)nOt%=݆?1ݕLH?.3/ÖMOT,XpDp%m$=hHQC9jv!U>)=6]PgǕ pBv0yUHo֠Su^'%Q2Wmou(̻%| |he@ci+@2DGe$:DW6j]ZNÓoCwW˙5ht(!uS[_KtĹ mIyK.Y S@0͞E,.1As=l9e>bvyulVL.e1ryCԠfhK.uPͩvY~5]vj7"$]C  a<xKzĬ4J"8a|Pq𶕞49`UDh~`(A>qwˊ z/ݦI#pL8b%z<*UlQ1jTj I* u0={|}tk[ɔEۣE#YU\8`Lp%}r:vř8kک()0ZZ%p5WMGKf/ܱMTU;wұ-.m3 xĮȉߛU7?Y$PnK +8yj6݆~/Ⱦ^8lq=`n\G{}F k+\X'R:+Evgj 8O/T<Ϛ bj[lf c KU )$b6Ŷگ3Vfs[@gp翳:-繟ܼ-gUݷ}M۸g6dASݭNt-WΟVQu`kM<Ј\Wr e-Bw8ʮTU7T\"t:]ESt*(4d}-#{D"'֓<(/ ;-znI ˃aBk52/R0 k fF>~r{ƟC^Q+eڧBpj-ڜ <7=FFFy1bLL8j e50w4aGS /%z D6ʔA ؾܗOEAZXdRq;[}.xΜAbl緰Z2.ՀF"~[Z|~d?~2%ŭAr( Td:#aINvAMɴ|"dJɗ)InL'LDЙ<|>+H`2$'{^1wC)%I\2/(v")(KQpDC$/E"OI&QL4c190%'X &0#owvj+j޽;?~aQjmIs_YJI*-+NT?,mɶܮ\u(yԩ#7Kהo)2 E(_X,ٽٷgst:S~'Wh(.zC`_N "--0:Y`{N*A3Ұ(G .1äbԌ*IT5&'iT ͦ:+{KA⿰̌ Ǥt*LIi@1غR⑘9{ wwho(6 :AHhgۂ.76U`4Tk0dSSm,`U3z+n5sa{iSқVT\ G@Q iuvVqi<4_ްНm1w;:L6˵$kUjj[N{O'y=P,z-OKWJ/J/J=bBJHrwlP:,[*UbX\Kbf\_H5^:;whU̺{Z ;G?CX*,i =as:Z8c%^pCU쀬}7)P$=j;gE հlEoEmm6ަ=:-jj9-iQ MڛTk}ڨhm>( $Н3GjTU@št /Tž 45)Q>FM@=GEcJ*I&?7 r'Kj'h l'٤,#=(ݓ8(f hf>ՄbHvf,pp^SwKu޺hIk{rvQkvS*7$6iIܼ(zpAgxcQ}WEwՔo<=Qzgw]Ep69#ܕ seyq=?qyF0Iڨ1.3B͜.HB7v 5L`2%ƄLsrllaɨ#GH< (S䟡>e[_N;ZCeHK^_;A],F{7Cyj0xp3yQ[mEe7Fw7Hm-1o37i;+Wۑ=Klkl6搃s3zzMMTT.{寥jY#jr 7(Tx2R ;(0ndzdp[-\qeMJ.)֢"j g%!#ZYfje$O%prE*MT%bj`jXoS ?B|*0(l6NpeB&'ڣ5QNEFᜄѪUV^CUj1s f29<&F]Ӊ8QP,aF6){bK ̹áv.M,*p!fA\Pƌ(/F ĘD@X;s4Y60ِhw|kk9 mZzw^]/$;V]S;eݱaCl ٜ7~jYsQSp4koS9/Ξ,mjRqhK!҆u5m0L-N׺+(e*]mo^^ NsHu;" DgKpedzəQă%!)$ גZ[oLđ-IG\G?)wԝ8 X 4' '9[ow jTOj2Nh֋bid/ :h6YMWDy<ʬQk+o=}&[K|;'x__~~kϸOd~?nѤT42a /;鱪,D4Vf&VəN!y?|IvajPS*(JÛw~xX}~XA  Q?̠@,?XDؽ?~ X;ODO(bW=M?!C#?Ur&ׇo+mE 'PL!'?|/<2c,sIm_@\o͠;=Jk~v+~Ox=_0Ns^oDy_.^)/E^:;_n=T^g~@Td(? fA&)/&a& pABkzҺg|@)5 V~#Pa}ɲ f5&ѕ/gx'Kz20(^V֡~Q{% FNaD"sP8hގM.WF]m@yJNSZn7XE(=&xNi7_f㮥n\:^j׸=zzV@;}:pܵ"?Z y*R@0@|{}G|q\~K 7b,uvzSy72Ĵ9%0 J 7ξ[ ͚ ^'vTpV=XOxwvVZ1s싯Kwmm s|kn~թ=&ʍ1=*έJҰ^PkxE?uKa?%\ hN4s;a,~ŢL#f>9Gڸݖ&;5US̞1/> epTIM`mm&VW8pqOqC/ĥjbQٞYA%gk~jSyEjΤՃ8j- ɮl8=^E8jQTbB6g +[#kP2ePeXV++b8/`hCL!Z C|'/`u*O3:$<CP?x.A)9!H6w!V`cV)̷GG{u3 lŸB|//+TKiH]8رcU^!¿?kj$,LVtY|Rtx8uG혾ѧg(F%}W0a 0|]:DޡrycYN)_xveH n/' b&wsS7QB{_# QR`F@M:ZEc*} *Ido0mDh**liI{?$}٤11!Z&?4˚68lvDG6 FIH_ n`%( [~:IV'ZA n`v SIm ͔٤E\ub[*Y (}&$X!AEx%HKoK0?)xIpE$ g$8*YH V/PoJ+uܪ5S $A&J^S%ϤVnmee5(ScAѺs8:N=Uƕ >b2) 6j봁f}O煋 pBnQY r3}`fs) n W2sP^Dj0Mɉ>eSldtYbT{Iida$b|^i_^0_W|ɑšBT͗5M7xYATku%:%+׳KemU})EMeSB-r{;S0zb<\jw)sِc)Y,,,*;3q4Ҋgd+O/]eec8>lV#LYCxH^\EEV4KZӽ^zϧIv`!/7y}N-ɉ#EpĮ-e sGK(!n|Rԑ2h3n>6De~80Kda>Mk }vЦ zfȵAfU2LndĖԝN+u/O36wi8&.8/98y'mtBqR3a H gtm ɷ)D`SoTq2VFoU.[kaZ3;;/V+IT|oV$/U9Q Rl{oVb_P}}㊼QשTH:ГTBXV :a['IUfilQAҙؚaP۠Z-BF˱M:O n@e`lqФx{sYu 75{-sNzG7yY7l Mܷ7rrT̼AL vc_IySٴ2v,jZQz9yґnwQnFN!g*-J{"ßGbV@AebBA3'fK̖͛BK֩~ѻkQ፿M>]%cwƻŻ˥ƨIL+Zi&OI%TYRRic="GH@@XYbHVAۘ;{O]^*77+#5u#b콅!RCWg_ݛY+g&P?-_7PRdhZRZ\r]wێF>O~t%]in`}~c-iC䋬.)N=`xMvTH|]`ުKsz++*{˧he. oUtJ9%c|;K _Ff[sJ>H\}.h!u 01+ )es[,Z*NȚe|p>7S> J,v۳*tQ5ͼYP(P9;gg95 N?;Vo (%2E F~ܿ|RY7S;F5?,'VM-'ճ߉w"uGH5-'xk 9eǣ`:Z)K;0OgHU!,?2fg&݉F_冄U'1A}x-Ƽx:F;("}'K#\-~,EV$M9[ID-n. w./Oc##$:(76s[Oi5DʄxZG bӳܢzz`ppߦͣn߆wv7lVFý#;{{K4/hkXqwG{zwoulҾݣ}#u6,;s;pU#,04?| }0ͦaw_nuw[f 6lطW;<ڍ[v m`z`xp ްޝeݣ#GG7tS~wxoGFw moݾm)6C[38Xb0^7C?7`nok_0S%^44vaG6dl 9x /T3E&Qҍ#d`QzڋםJYK-$5mJ:1Ocl0يyd?)_Þ!|?>]wu}7 dR0B)mGa?]Bb[-Ixf7jQ,66, d{%BP^Jm T jl7*AhVfֶeWVv#oTQmH^J&}8}>6bylL[uMvLgqDTA|?\`@>hRsWp}4{}݋ ;; 3J۷\uk]kuZ+\W%Wj5ϭ _q.G?t&|1+cps%͹P $i&'=g&&&Uz_{3O)<Oƒkg.r6כk]2^X0݀UJ!RJܙ,H] <ַ#ʝe[|w^,/ߓITث֞g7rkvgBV' *S,!>"^_x(lo8 |u%QVyt?+ob|˙N¿hE .1ڱ[6!& Q6zqOA}JY!FdvURCR__NM סF)Ҵ^>7 endstream endobj 369 0 obj 18526 endobj 370 0 obj <> endobj 371 0 obj <> stream x]͎@<a30ek%xR >S]Do30KmcXs?ts )\!3w};fʊX{|ܖp= qʊmӦOSV|0%=}~k s3}iЪC_93|<cnSӆ.![:_, |ɒӹj,:jQ]jWЕjUK UWp+=:wYy Nm6jSrLGCz M~X!}_.C2I~/X!Z;K~sV=)) ~)ZK~~[0 -{d+)))7?k!%M36#OK~|,+߁2J=ihO;^K~m_{fK~~{cK?: r8 8]G.-(9|弽s<e'T7OZ endstream endobj 372 0 obj <> endobj 373 0 obj <> stream xռxǵ0:3^  $A )HEIDbQ-ѲEْ\bȖ\$۱$%MĎ}n+&qM,BH'y{=؝99!B3AšM8B=~͖)MiP_)#&#$n>!%itK#ausnԶ7B=qlsPk2o69ux>rlJ`"$&Qzz}|bh>0v=a%NHerRhy`4%-Id[Lu32=9yB_QqIiYyEeUu_o75/Ynm[ޱýǽnȄ V~Jk׎B;Dϡ(_D_En~Gw7#00έb~gچ㽎VY̠~4nGga4VAoa9{0΀g8GhԠQU& P$z ={*ҺJg(M_M|HyO00dmF=-~ .Aw3vXb{%2 "{Ko qLt]zp.@ sh5(5p:!Ѿ5ܲtIsТ`VTWUV{rs23iTbZZˤeF9WIP(]0p]CMDb7Ǎ=9|SO!SXyGq\ .Y \]%D,bE p,D[`JE~HN+PTB)?3kX [; V+71q5P8dDRC:FA w~j583 L`vvDd"Y;~mEr\ Զkͻ#Xҧ7 [$nsDAl:]U@;᮳W_;hpE|3BAǺhYfuepLNg*X݁V[ 銐~zS23e~`iYlu7@O)*\|Dg59ʽ]b_̪qp,p7[fyst H.p[Y`GnN$䉡3"4@A(p: w FDEUO:ȲNmc}XWE>ThzlpoNvtZ ꢄʺsOΦeβDbp;p0Nkl -st+yhpફcDT؊{4"YPC0(Gɩ>4?Va}rs\v w(PC7Hh#0Dai4t \!IF#B9 q\PX& HcEb}reǬմl`DIX(YE G<{Z(/l;ju-{ͺ>KpS{]n.|gi߹Uww!uNk:VB[i#8hЌxm6֜%6~@k6,YA~?v4"#Uq՜D(\Cui?.R ssv\Q8 r`%KQiUgl3 ":f6JlW`9un@4`ta bB#1VmTOY]Pwr$%3vbL0aoB}RA>5tFW SIW_}Y0mDX2_vIK{Cyb[#Cs^Jxm%9_vdEBzˬd5!Dq` $ݡ$A $ d(ZB%^}^P샹;MN1WXCJMƕ^;\6;2;Z'`NIOM4K*(Oxz1c(U!a+W IzB}lWvCТ,UK~FOy4ޢY .QhS/75|Ø,$ [c2WfwQURQs9|o<=prOt/`SMye#俤c*_aAS1ŗaOnjMW?eVT-$b\Gv``Qep 3lW/Bo`1%V[Yao/[o><򁑻WNv)*D諂6 HM!#۫T3bpU!9Ll\yrM1URWFL2^!B/B&l*oZ}9t{:NΤ;9/GRJ %`YG*ehTC.,J1(qq J &j7K5ɘ.ѦhR}yF9-3Oj-m/+lpKPλslGkұ1ɬ%E핎l8ԎUVmFO'lXyt4/*OwX6h֎gN aIM4&Xq&ȃ +"iKyp|z-1dvH f][ӾoeK*^/y^(\1V1y@l>eɛeSk,[`0]!54 BF n>`&x} ,,*XQ@Jq +$pd6fddP6*A`v :2dT_BE~84K1P6K锭) V9ȪKUM+ք| ;Tݖٍ'yy{n=7ȳ֬m֦*uY)]feNѼtn{UY.q.0a n`ǿcN^i8t'{RƒUMC1{f0#5TcYNXO5heJ$ ˴0W&JkYHnP4N:͌b'J6~:Mw9tr.tx M9:*ʾrmqB\J-! ~e^bz~/2^oe*Լ\Vcگ&?V^Œpbj&c Z ~3w hi%JuF}`tĠz΢tT|:\a zOY[^d~[B޲뽣uqfYjS}*.3f,*z ⶂgQgЖbCCi^=hơօrjxH%!(jF{IGÈw0/<)ӗSQ ^S)WXǠ@0;z !_r0_;eJG0ʹՏU ;ڲFnjc4dZА=* &AoO9BpDt=:2-+#E E"zzz}`z[՚aXDGbdqUb֔j ;4aήܥZ~UˏRc$Q.^`]ǽ e%L11,95PYYI5%RD5r…J $a$mAk5RfS mYghj{_\9<Ւ2Ѹhy?hѓX{Xu\G,*`lK [Y)!<mm6f8,L Ur8JeD 7  7zM)lb7S0iyq%9I,p(qQ|BB\o RKPbj<^ k+2Xj0Onzd {;;ؒgEU]ɳw|Pa,&=uy ӉaZ"# <6Fͬb9#1# nJ*1J%jb4a{%>ЭM@۲(Pjt`X&xgm}s?gW'>9c24qٹ?/uYDz9Q}wFwsμ4XR]KfX҅2^kV}%>TZK Ǩ=$96A ff\+ԍ"V\4$x~<[r:?Fd2;Z-/b 8! !lR.g4O"(|yk36.u| $)mЉ?_1vn[0sV~|HSav/#ƱYx 2J'b3L8/0Ao I-z>@X<-EIMS̯G[U2Q'&6V 'o]x"pp`*.O%8 _ BJ s/ *M+HO+yfԣyOZ5DJh2M!vA3#͗͜ߌ/@26٪>88`>P9jQ2> :hwFLnKꊭ/d9ߺx̦+/ vffvY|OQD^T7&[T͞2Up5WګȖ}d*cO"%D]H)>HvX`ʻwu#pMyӲXM' tb`D4 てxKzX8i7Fk3/)f[~"UU~֎{ WVvzrs g`Cxcg{t|mW]:8ݥݵi֊n̥S2jv%UMƞ]6J}g=$ 'C[\> ;- !S=TI6 jclSqjj~rV73}R&OwP./RZ\ҕ.M1D) .J`E'3XǦ;:̋я:rzw`Em=>|#=AܺlR#/[dns,OVd@g~4T*l!Y)Y`%mK3 v@AGY۝K2z2z:ƀFSnC7qmQ;zvF>admV4iŢɞLj&q匒8Og"Mޅ&0zBhHȗ<á[{o( elo;5n]p .+&OGJ38bh>kÎg=.R5&u+ S뇀DMQ+s:so&٢ڧ"ȝl)#[mmd{TM x 4^4jfOp F5j>ajjF/*.+HL|dyQz'۹Y{w\:'$w^v(Ki8Ƴ"mRͰO|ۭ/o- TOo|vﭮ^sk`cp?9b8|TJn\2p8T,lpϲEErVS>Xp[yEΆj(nL|޶*n ^ZV-jZ{-=s%:*M)(HP\1`S=Rn7[iD]+QSrB *!ˑJj˃#J EOJXv -/DS -Օ#OA~LV"ztbHe~M|yRg.0G{.$ 6!fzNZ_~X~\ζ仠cc\fe}R=j˜4voK~M )njG˔FUWUѷ/xǴl ۄ :?&ȟ'~PuTLSEOņ ؉9F]VaN20ϚYo05G4'4DIZ$ r]83U-$aaIԖS|mXk2!ɗY1$ $ˌ^|ƮLkaL:f&kVm^YS"e#+=G7-SSGBJx8\L_*5ioe6W}wݽxd 4{3|Wa>ؚ5n>*`${Ԃ4TWxtx:{o6ْ/lIݗJ4x lqOx}NrrsM ?M'ň5>-CHD 1w)RY1삝PᲝ i><49vIt2U*WqtBQ]Vq* p2G)e6tĐ /nPؽ=\6OB= 2y兛={?բl*٤`*Ԝ;18ss}ft'"Dm@gq's "XLvN^1\"É.QIL,#!m2XINr9&@&9$^Rer9 Ŭ_B|f 8o󍁃X#WvA?D:%*jo~ N~w)FMuOs?ucTSBΰ.Oș[ALqL#]?Uz#=q!VkSWRW21}Nj/={‹vwH<'XųSB"wvc:`27Vt7z^?aqut{QGsGEV\g)u#fr2<!'|1IʮTJ6-tx>%bye10y;g_ |葨 W<5z~4r!aՈl쀌n2f0tHpJME&w>p(F MɔיwI#ir,gfiN ?q[Y4D/r { bXE< MoDP쥛C͵戗qY31-ASfSnZFDWDՁqqUtZKC0V]x5QF!r. P{a˄\/YLmKpf(3U샯cND=f]Jh/,:G;8'z6:܈7JuojƧUlw&E+^p hB`ZKa(Wr\BhpHQjc :w.Qa酥rbRR(%K,!?,h a9q7OϾ.GNޝum[x_Dxhncx\B08G)},qη8Dah+aSVB[fGtnpp5S/,'/I7 &9E#&n3YdlKJkq<X `D&D.I\6X =$9*}˰!ry;r1H)@|ȳX}wYCܽ0з>4~)G\B$2s1ax>?1SVcInAp^bĄo'Oͭ<>?6+by:n%1MSdMi^;B%1 3bH'60Y'4EŚTf㿵oQ7'aשMDe~R}uu33GjFd<' nW('$Gbkuc}4b;fY!qOI!)$-(بFXl2i"$;d!Ɍ-97:Q&[PjBIThpv:i^5a0xqA1b 'MZQըq[rBrTm1nlRlb,WVS62f#4d39a).b"MNN֕07$1҈[`>xk~35|}^, =c [-7mlK9e kK5TRZK P3@2mܨ{[t7wK rBR1C/ր ^}= 㖎,W?]a\K[HDڞ` ?iXZKBU%l !M3h_Tũz=NT"MKKY)n$gI3 $ L$LbiœDׇ ~E;iFtyvn<|pI?xʿ0: džn2Dm̝ ֵյŬMݚ?5"/Xv}8ٚc2Qrc=83vidUdO90p8QdJ ?|W*Bn9,XK!PscQεK'oi]=dcU [}Mj׆o Ә?ۗR?=}5'6ԏskC.m43 ea]A.aVJqI h4K,pq<'i0K4\ ?9L"i!}b+ r #0GTwtYЃ-O VƢDF4}OOBsgI=Jy1ѯr!CTkp^r#1?_Puuzms|?F RqYcPt29u0b.ڨsyMEl+gCܣB,2ZB,<=(ULxQ`XthIxUJ-jNG^1AoR%&^+P>@z_PqRA%F9X-5)6:\^)XUm z#ȓ_ rojKj_*T``'2d{V29ݹY\JϪP%j(XOГPc&Wʂ a2&7̦eF&7\ı535G6S~"c!ljCZAiissgr0 c.\*MMCML1?sSߚ”"hQ\h'hɦ:s.RL!aq_rT^՗޴Emp,1뙾ǂ{6/EVnzl wդز~VV?'Hy &_}Aν(۽De(cAc&1+&6SE;3gH{-'E#SX,SSzp,nqy 7v=%-}oX[OBPcmDїR׀ilӂ:3ēbb:~Xx 7]c͖ cMXۻ<:dy>X׳'?ﵔ䦌C_TwQ@)[9Q'WȻ\,H$DVpey!WSD=Noyw3w>-W <7*ИRӽܬ4ZB*TK8*杧\EI.6oEaG?~cⴽ0rpy}(1So70J^oF=}z4ZfIm}ZGc }boNnF "Nuc{ouaFh}Ʋw{رz~ ѿZ"&'G}*i/?ߧp\wF^w=x ue+vno.N8X_uǘw'ҵ-+Z>&nI1^E)@ mRVvy6BRRJKKYDGy)Z_u%JI2V̥ %&-T`]6&y!:d`j׬JJKJ=m8[xc8߬4NSˋ 4rҫr3!"̠o}8w0=C-VQyIzN^FWIbї5ne  d\`.͔67U8;Vtsa>b8a CwM* '0x¥xҗ1v]1H{T@uu-BOQH,o>sHm@-?Pni.8!/M,/p1̆_W*R}Kp"66wqc2}#?$c_jW׼u _cQ?/< e N9qft)1x]K).@<+"?}-G7y/>}^7->/M0JY]cv/|٫x>RO[e;?ۙ9alb<'?0{@|/ba\,:! cC"EXd"cSHeK n2 "R#!RN۔,*W)8S*;1 #ArV&nOZ!U5$CrFbW`вb7Hߡܳj]}[2]?z?:=>(}[z-MG)ɮ`>mf"c.8{= !øc6 bN{K#} +N}aac~t?݀4-hiI|ÒXq0Rg8S |ܜ_U*.#snZUjLVC[4 fi/=|X:N\LԨwb9q!N_87vRwoR/}[mȪ[YVqtENxW$$ty d4-.>]2Ԑ[m?)䙑Ydj:AOOX|-.CL\[cnxĖ[Ћ-Wo(|.=11$"ڏ{Z3k~]-%ύcRp8(;?c? }ڪϑ=m߯p?s7dwD$=}0QzC/h͠?Aiա Qm%>ڋpzz t7= xOUS{Jig4 3[p Mn4YlQ7Fv4{ AN_c ΁Ļq"gfs+{K~@R$4$}N?dE=WQ(V\QTDOP]CKm'Ot)e2xDL){M?O$<`gb4q_ҭI?+6Bɟm~_o8d{Rԥ[DAL2u#2fhJҿ#P@2Жx>Ge/R/ːrbWdFcxYjOǬjxF/are12 }Fe%g(?/Kџedr^/J(^VsGe U2Z?6}bd)G,Ga~~mhq4sn;L:&&& 9m-K#Ц oY=410526X26:6vzD䚡 G7UMLrA^~iś_LfvdrjhGFyၩ)蠣}Ɩ5Cb㚡<6~zbdrpd }d&bsX65eȱd`jjhrltx׻uּx57o&?6}|hphrd(,1~hTZ֑ #x#k׆ğv7GA>L[ȁ2A>fdz>*RBpF4 T6Ÿ&El@=PnA|B<6@fz(\]"ւD(W OZ-3&_84?\2^3Cy? 1Gǎ^aN )ibx0Oavq)F8<' ͯmeSڛmDli^['Qh/*AG^7O,mO8$by-a;fW o|A9Ù?3̶_?{fg?# [?lg içDUu_W~+_쿸~{z.J؈ju`f:>Yϫ?xVDh | 8EUBq':a s3g,3G3E>bM>{;cz{k]o1翅NÎs.c͜#7o78opnw?>;~v,'FYCV;^ 2JvD^_<DDH$r!a"'^@. {_xHkQ!`i`I8|Hü`cƏ1=n0n?*%4:@Br'.USpPiugqװ ,3 ޝngUGpl?=BƎ:Ba0Cu$ɿ k彋 A> endobj 376 0 obj <> stream x]͎@~ 7=3=fWBH,,(léH9j5ߴ]l}MŷҔzӹ3k&[sk۔.t].6i^SV|4v99}~KꧼVMs=|/Ъ};?\/16MeYnRb9ͩeYfU/:PТ:jX/kWoԯ-g\}vԛY`s;_P/t!ǾAs4A?B8GO_kz[A_4^"B"6~4~4~%|G6?8=}з@~@qǾb>/g Ư>`= \b?g=| ?'>B^ g/+!ǹ#gW"?nuPDL3>8\T7w\5 endstream endobj 377 0 obj <> endobj 378 0 obj <> endobj 379 0 obj <> /ProcSet[/PDF/Text/ImageC/ImageI/ImageB] >> endobj 1 0 obj <>/Contents 2 0 R>> endobj 4 0 obj <>/Contents 5 0 R>> endobj 7 0 obj <>/Contents 8 0 R>> endobj 10 0 obj <>/Contents 11 0 R>> endobj 13 0 obj <>/Contents 14 0 R>> endobj 16 0 obj <>/Contents 17 0 R>> endobj 21 0 obj <>/Contents 22 0 R>> endobj 24 0 obj <>/Contents 25 0 R>> endobj 29 0 obj <>/Contents 30 0 R>> endobj 34 0 obj <>/Contents 35 0 R>> endobj 37 0 obj <>/Contents 38 0 R>> endobj 40 0 obj <>/Contents 41 0 R>> endobj 45 0 obj <>/Contents 46 0 R>> endobj 48 0 obj <>/Contents 49 0 R>> endobj 53 0 obj <>/Contents 54 0 R>> endobj 58 0 obj <>/Contents 59 0 R>> endobj 61 0 obj <>/Contents 62 0 R>> endobj 64 0 obj <>/Contents 65 0 R>> endobj 67 0 obj <>/Contents 68 0 R>> endobj 70 0 obj <>/Contents 71 0 R>> endobj 73 0 obj <>/Contents 74 0 R>> endobj 76 0 obj <>/Contents 77 0 R>> endobj 79 0 obj <>/Contents 80 0 R>> endobj 82 0 obj <>/Contents 83 0 R>> endobj 85 0 obj <>/Contents 86 0 R>> endobj 88 0 obj <>/Contents 89 0 R>> endobj 91 0 obj <>/Contents 92 0 R>> endobj 94 0 obj <>/Contents 95 0 R>> endobj 97 0 obj <>/Contents 98 0 R>> endobj 100 0 obj <>/Contents 101 0 R>> endobj 103 0 obj <>/Contents 104 0 R>> endobj 106 0 obj <>/Contents 107 0 R>> endobj 109 0 obj <>/Contents 110 0 R>> endobj 112 0 obj <>/Contents 113 0 R>> endobj 115 0 obj <>/Contents 116 0 R>> endobj 118 0 obj <>/Contents 119 0 R>> endobj 121 0 obj <>/Contents 122 0 R>> endobj 124 0 obj <>/Contents 125 0 R>> endobj 127 0 obj <>/Contents 128 0 R>> endobj 130 0 obj <>/Contents 131 0 R>> endobj 133 0 obj <>/Contents 134 0 R>> endobj 136 0 obj <>/Contents 137 0 R>> endobj 139 0 obj <>/Contents 140 0 R>> endobj 142 0 obj <>/Contents 143 0 R>> endobj 145 0 obj <>/Contents 146 0 R>> endobj 148 0 obj <>/Contents 149 0 R>> endobj 151 0 obj <>/Contents 152 0 R>> endobj 154 0 obj <>/Contents 155 0 R>> endobj 157 0 obj <>/Contents 158 0 R>> endobj 160 0 obj <>/Contents 161 0 R>> endobj 163 0 obj <>/Contents 164 0 R>> endobj 166 0 obj <>/Contents 167 0 R>> endobj 169 0 obj <>/Contents 170 0 R>> endobj 172 0 obj <>/Contents 173 0 R>> endobj 175 0 obj <>/Contents 176 0 R>> endobj 178 0 obj <>/Contents 179 0 R>> endobj 181 0 obj <>/Contents 182 0 R>> endobj 184 0 obj <>/Contents 185 0 R>> endobj 187 0 obj <>/Contents 188 0 R>> endobj 192 0 obj <>/Contents 193 0 R>> endobj 195 0 obj <>/Contents 196 0 R>> endobj 198 0 obj <>/Contents 199 0 R>> endobj 201 0 obj <>/Contents 202 0 R>> endobj 204 0 obj <>/Contents 205 0 R>> endobj 207 0 obj <>/Contents 208 0 R>> endobj 210 0 obj <>/Contents 211 0 R>> endobj 213 0 obj <>/Contents 214 0 R>> endobj 380 0 obj <> endobj 327 0 obj <> endobj 216 0 obj <> endobj 217 0 obj <> endobj 218 0 obj <> endobj 219 0 obj <> endobj 220 0 obj <> endobj 221 0 obj <> endobj 222 0 obj <> endobj 223 0 obj <> endobj 224 0 obj <> endobj 225 0 obj <> endobj 226 0 obj <> endobj 227 0 obj <> endobj 228 0 obj <> endobj 229 0 obj <> endobj 230 0 obj <> endobj 231 0 obj <> endobj 232 0 obj <> endobj 233 0 obj <> endobj 234 0 obj <> endobj 235 0 obj <> endobj 236 0 obj <> endobj 237 0 obj <> endobj 238 0 obj <> endobj 239 0 obj <> endobj 240 0 obj <> endobj 241 0 obj <> endobj 242 0 obj <> endobj 243 0 obj <> endobj 244 0 obj <> endobj 245 0 obj <> endobj 246 0 obj <> endobj 247 0 obj <> endobj 248 0 obj <> endobj 249 0 obj <> endobj 250 0 obj <> endobj 251 0 obj <> endobj 252 0 obj <> endobj 253 0 obj <> endobj 254 0 obj <> endobj 255 0 obj <> endobj 256 0 obj <> endobj 257 0 obj <> endobj 258 0 obj <> endobj 259 0 obj <> endobj 260 0 obj <> endobj 261 0 obj <> endobj 262 0 obj <> endobj 263 0 obj <> endobj 264 0 obj <> endobj 265 0 obj <> endobj 266 0 obj <> endobj 267 0 obj <> endobj 268 0 obj <> endobj 269 0 obj <> endobj 270 0 obj <> endobj 271 0 obj <> endobj 272 0 obj <> endobj 273 0 obj <> endobj 274 0 obj <> endobj 275 0 obj <> endobj 276 0 obj <> endobj 277 0 obj <> endobj 278 0 obj <> endobj 279 0 obj <> endobj 280 0 obj <> endobj 281 0 obj <> endobj 282 0 obj <> endobj 283 0 obj <> endobj 284 0 obj <> endobj 285 0 obj <> endobj 286 0 obj <> endobj 287 0 obj <> endobj 288 0 obj <> endobj 289 0 obj <> endobj 290 0 obj <> endobj 291 0 obj <> endobj 292 0 obj <> >> endobj 293 0 obj <> >> endobj 294 0 obj <> >> endobj 295 0 obj <> >> endobj 296 0 obj <> >> endobj 297 0 obj <> >> endobj 298 0 obj <> >> endobj 299 0 obj <> >> endobj 300 0 obj <> >> endobj 301 0 obj <> >> endobj 302 0 obj <> >> endobj 303 0 obj <> >> endobj 304 0 obj <> endobj 305 0 obj <> endobj 306 0 obj <> endobj 307 0 obj <> endobj 308 0 obj <> endobj 309 0 obj <> endobj 310 0 obj <> endobj 311 0 obj <> endobj 312 0 obj <> endobj 313 0 obj <> endobj 314 0 obj <> endobj 315 0 obj <> endobj 316 0 obj <> endobj 317 0 obj <> endobj 318 0 obj <> endobj 319 0 obj <> endobj 320 0 obj <> endobj 321 0 obj <> endobj 322 0 obj <> endobj 323 0 obj <> endobj 324 0 obj <> endobj 325 0 obj <> endobj 326 0 obj <> endobj 381 0 obj <> /Lang(en-IE) >> endobj 382 0 obj < /Creator /Producer /CreationDate(D:20140324141628+02'00')>> endobj xref 0 383 0000000000 65535 f 0000399797 00000 n 0000000019 00000 n 0000000468 00000 n 0000399943 00000 n 0000000488 00000 n 0000000935 00000 n 0000400089 00000 n 0000000955 00000 n 0000003992 00000 n 0000400606 00000 n 0000004013 00000 n 0000006372 00000 n 0000401013 00000 n 0000006394 00000 n 0000008429 00000 n 0000401196 00000 n 0000008451 00000 n 0000009446 00000 n 0000009467 00000 n 0000040627 00000 n 0000401344 00000 n 0000040650 00000 n 0000042144 00000 n 0000401519 00000 n 0000042166 00000 n 0000044483 00000 n 0000044505 00000 n 0000050173 00000 n 0000401686 00000 n 0000050195 00000 n 0000052235 00000 n 0000052257 00000 n 0000062033 00000 n 0000401834 00000 n 0000062055 00000 n 0000064667 00000 n 0000402017 00000 n 0000064689 00000 n 0000067035 00000 n 0000402165 00000 n 0000067057 00000 n 0000069028 00000 n 0000069050 00000 n 0000080876 00000 n 0000402313 00000 n 0000080899 00000 n 0000081628 00000 n 0000402461 00000 n 0000081649 00000 n 0000082955 00000 n 0000082977 00000 n 0000092683 00000 n 0000402609 00000 n 0000092705 00000 n 0000094102 00000 n 0000094124 00000 n 0000115854 00000 n 0000402757 00000 n 0000115877 00000 n 0000117917 00000 n 0000402905 00000 n 0000117939 00000 n 0000120777 00000 n 0000403053 00000 n 0000120799 00000 n 0000124127 00000 n 0000403228 00000 n 0000124149 00000 n 0000126469 00000 n 0000403395 00000 n 0000126491 00000 n 0000128996 00000 n 0000403543 00000 n 0000129018 00000 n 0000132227 00000 n 0000403691 00000 n 0000132249 00000 n 0000135033 00000 n 0000403839 00000 n 0000135055 00000 n 0000138545 00000 n 0000404014 00000 n 0000138567 00000 n 0000141627 00000 n 0000404162 00000 n 0000141649 00000 n 0000145268 00000 n 0000404337 00000 n 0000145290 00000 n 0000147405 00000 n 0000404485 00000 n 0000147427 00000 n 0000150326 00000 n 0000404633 00000 n 0000150348 00000 n 0000152620 00000 n 0000404781 00000 n 0000152642 00000 n 0000154180 00000 n 0000404948 00000 n 0000154202 00000 n 0000157172 00000 n 0000405117 00000 n 0000157195 00000 n 0000160017 00000 n 0000405267 00000 n 0000160040 00000 n 0000163105 00000 n 0000405452 00000 n 0000163128 00000 n 0000164899 00000 n 0000405602 00000 n 0000164922 00000 n 0000167490 00000 n 0000405752 00000 n 0000167513 00000 n 0000170726 00000 n 0000405902 00000 n 0000170749 00000 n 0000173962 00000 n 0000406087 00000 n 0000173985 00000 n 0000176843 00000 n 0000406237 00000 n 0000176866 00000 n 0000180369 00000 n 0000406387 00000 n 0000180392 00000 n 0000182193 00000 n 0000406556 00000 n 0000182216 00000 n 0000184361 00000 n 0000406706 00000 n 0000184384 00000 n 0000186585 00000 n 0000406856 00000 n 0000186608 00000 n 0000188365 00000 n 0000407025 00000 n 0000188388 00000 n 0000190149 00000 n 0000407175 00000 n 0000190172 00000 n 0000192145 00000 n 0000407325 00000 n 0000192168 00000 n 0000194189 00000 n 0000407510 00000 n 0000194212 00000 n 0000196555 00000 n 0000407660 00000 n 0000196578 00000 n 0000198128 00000 n 0000407810 00000 n 0000198151 00000 n 0000200691 00000 n 0000407960 00000 n 0000200714 00000 n 0000202108 00000 n 0000408110 00000 n 0000202131 00000 n 0000204153 00000 n 0000408260 00000 n 0000204176 00000 n 0000205482 00000 n 0000408410 00000 n 0000205505 00000 n 0000207036 00000 n 0000408595 00000 n 0000207059 00000 n 0000208930 00000 n 0000408780 00000 n 0000208953 00000 n 0000210498 00000 n 0000408930 00000 n 0000210521 00000 n 0000212487 00000 n 0000409080 00000 n 0000212510 00000 n 0000214285 00000 n 0000409230 00000 n 0000214308 00000 n 0000215564 00000 n 0000409380 00000 n 0000215587 00000 n 0000217557 00000 n 0000409530 00000 n 0000217580 00000 n 0000218910 00000 n 0000218933 00000 n 0000248146 00000 n 0000409680 00000 n 0000248170 00000 n 0000248540 00000 n 0000409830 00000 n 0000248562 00000 n 0000250056 00000 n 0000409980 00000 n 0000250079 00000 n 0000250448 00000 n 0000410130 00000 n 0000250470 00000 n 0000252359 00000 n 0000410280 00000 n 0000252382 00000 n 0000253735 00000 n 0000410430 00000 n 0000253758 00000 n 0000255190 00000 n 0000410580 00000 n 0000255213 00000 n 0000255583 00000 n 0000410730 00000 n 0000255605 00000 n 0000256574 00000 n 0000416264 00000 n 0000416383 00000 n 0000416504 00000 n 0000416625 00000 n 0000416746 00000 n 0000416867 00000 n 0000416986 00000 n 0000417107 00000 n 0000417228 00000 n 0000417347 00000 n 0000417468 00000 n 0000417589 00000 n 0000417710 00000 n 0000417831 00000 n 0000417952 00000 n 0000418073 00000 n 0000418194 00000 n 0000418313 00000 n 0000418434 00000 n 0000418555 00000 n 0000418676 00000 n 0000418797 00000 n 0000418918 00000 n 0000419039 00000 n 0000419160 00000 n 0000419281 00000 n 0000419402 00000 n 0000419521 00000 n 0000419642 00000 n 0000419763 00000 n 0000419885 00000 n 0000420007 00000 n 0000420127 00000 n 0000420248 00000 n 0000420370 00000 n 0000420492 00000 n 0000420614 00000 n 0000420736 00000 n 0000420858 00000 n 0000420980 00000 n 0000421101 00000 n 0000421222 00000 n 0000421343 00000 n 0000421464 00000 n 0000421585 00000 n 0000421706 00000 n 0000421827 00000 n 0000421948 00000 n 0000422069 00000 n 0000422190 00000 n 0000422310 00000 n 0000422430 00000 n 0000422548 00000 n 0000422668 00000 n 0000422788 00000 n 0000422908 00000 n 0000423028 00000 n 0000423148 00000 n 0000423269 00000 n 0000423389 00000 n 0000423509 00000 n 0000423627 00000 n 0000423747 00000 n 0000423867 00000 n 0000423987 00000 n 0000424107 00000 n 0000424227 00000 n 0000424345 00000 n 0000424465 00000 n 0000424585 00000 n 0000424705 00000 n 0000424825 00000 n 0000424945 00000 n 0000425063 00000 n 0000425183 00000 n 0000425301 00000 n 0000425419 00000 n 0000425581 00000 n 0000425740 00000 n 0000425902 00000 n 0000426067 00000 n 0000426221 00000 n 0000426378 00000 n 0000426538 00000 n 0000426681 00000 n 0000426824 00000 n 0000426961 00000 n 0000427124 00000 n 0000427280 00000 n 0000427402 00000 n 0000427524 00000 n 0000427644 00000 n 0000427766 00000 n 0000427888 00000 n 0000428008 00000 n 0000428130 00000 n 0000428252 00000 n 0000428374 00000 n 0000428496 00000 n 0000428617 00000 n 0000428736 00000 n 0000428858 00000 n 0000428980 00000 n 0000429100 00000 n 0000429222 00000 n 0000429344 00000 n 0000429462 00000 n 0000429583 00000 n 0000429704 00000 n 0000429826 00000 n 0000429946 00000 n 0000415663 00000 n 0000256596 00000 n 0000262485 00000 n 0000262508 00000 n 0000262705 00000 n 0000262997 00000 n 0000263161 00000 n 0000264825 00000 n 0000264848 00000 n 0000265042 00000 n 0000265344 00000 n 0000265512 00000 n 0000278699 00000 n 0000278723 00000 n 0000278929 00000 n 0000279445 00000 n 0000279817 00000 n 0000295009 00000 n 0000295033 00000 n 0000295247 00000 n 0000295807 00000 n 0000296227 00000 n 0000311901 00000 n 0000311925 00000 n 0000312130 00000 n 0000312737 00000 n 0000313195 00000 n 0000335458 00000 n 0000335482 00000 n 0000335680 00000 n 0000336346 00000 n 0000336857 00000 n 0000350560 00000 n 0000350584 00000 n 0000350785 00000 n 0000351367 00000 n 0000351801 00000 n 0000360047 00000 n 0000360070 00000 n 0000360280 00000 n 0000360688 00000 n 0000360964 00000 n 0000379579 00000 n 0000379603 00000 n 0000379813 00000 n 0000380418 00000 n 0000380870 00000 n 0000398096 00000 n 0000398120 00000 n 0000398316 00000 n 0000398975 00000 n 0000399484 00000 n 0000399620 00000 n 0000410880 00000 n 0000430066 00000 n 0000430226 00000 n trailer < <53082F505C8B6DF496D5C60AF895FB02> ] /DocChecksum /A9128BDB763FCCD72A6550A69FCDDAC1 >> startxref 430479 %%EOF aprx-2.08.svn593/doc/aprx-requirement-specification.odt0000644000175000017500000010256212005774516022075 0ustar colincolinPKP<^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKP< content.xml}rXv|RDrfEV.]-$l$A @Q Gۼ#eϪ/{qA$@ 21KWrνg_Ǒc~8PОYţc݇l:VͪꗗgT-O|,ɂz~&Yv}$w2Z~jИ9aD< woGo; =e-lvb mqp R K`jAKkn6~;]#XÉc;]{**b)zҟ4?5//7_"BnNB^Qolw8d]'VN*AB kGީ=X}nű v?ϛTӱ ];mN[명W_&.,g2rgv?oD.Өv:>~[N%Biͼs_iں,[is}RN}jXV^˾ҷ]\FVϾҗɴҳgvLc&Zv)iBA4 :oodcojp=Co'kۂE^ qȚ1g4c+&,>бC e'30BtEp咭ma) T: K;i/}YF׮ʴ\wN7&Ds\':K'`jI̸]U6e/±+w~7䀡vqavOqx4cvHVAb>P>i"yǀ k B@p$M۳b˫.P"DCMihX#; klD}6[ pb 4e=o>r. p9;ons9F'nNu-?ca}uQ6d@f\#JaV$.J{=K<ᛑXxپ^k _ fS0wcPm-sk <… m߮L'62(t?Te}مXK>R,\zF]S:\"P4?D TV*&>q PaH0_qYwns=ee}lѾlm,0+&wh;k Rpk907AN6;$#0# ""T C?/$-[oZ%ɯf<1}cu/7[,H\ߡFw]ZK]Wpt?8Z:8^}+i׷(i}yol-IXr ǵ-Z;75~6:?w鹐d֒u=^̵d fɭč룼\ݜl ES;n[rHmpw﮽ymQ:`:/ ͽ !zybB{sWbfU;ou_}{_w.m ޢ<\8٢܊4(}k˧ޢjVLz%_8<⤍ffv ٦ZMuVk6E۴||d#hoQmd梻6˥rr|֮Fm[ZnQ"l,6irI%Z.OS6 T#6vb5)7I'|k#ucVyPmQxo,ooQm<"-kSUEL9CŴ- =lg"c}u@٢$xv;iS~iaEP`}9[/ @l*BgEY<#98̩e|}D"|;iP$~0y܁gE}M:2wsi}.5e5Y=Y{#H}͑KUy틿0)`iES)fi9Eo}DFWY5CN V n>{]~G68nֆc;Y:*KJ1Ȗ_(3_@;ltĻ$5]7}7%m9o6g$c׭uPǽvn6il"`UqoѯΗNGɯfh\(aFĭ @[N6TQ PGU\#94ܺfCmRru2aCBhEp4yC5܆!*d0mN_^ʤ bJ0#p.<&76Ov168ǛZJPe,Q1 yV4I} O#%"d]Yş ]]]j [⋝!U"K"Yg[򓝡]+BE=+苝!S"K"Um2-u^K]]dÖbgȺ(!뼞nOv]]j3#;CzKlIl]^dF)iQ?|dY`*AVi(JGU? Q,l,GVi(JGU? QdB Y@*VQq0 HF/}cVz4fDy =!I"Vm]^ (.VM'viP/;t6M7VcNjwi_/.w$pǛ馓to}sNTvjc{Nwj|+Il5>-J?wm0b\J1J1J1J1(u^;Hs΃-}W;EڷSXl<AjHVS LwoTpqHP [;Cڷ:[`*(};B*@5營 L9  59;  9 3 9; Ÿ(y *(R+RKJJn3U JRUVI**ICWY%0*$ ]eB!T tUTI#4]@* CWi) JHU@ R tba0zi) _>JHU@ R0t 4 ]P* BWi) ci#4]@* CWi) JHU@ R tba0fi) _>JHU@ R0t 4 ]P* BWi) ci-4͙b4 u๡mh <ѹޞd(ι tQ=;^\ޅmf(ιtQ-{^s#x6lm}^s?B#x{8&FS{Epy{'6m8!]JCXi+ a_KC4}. a_-Mn4*2v wOqqdr*Sd\<nbkpY;O[q^V*ݣT(GY @PLe%>J@2~ d(+QV*T,Ⱥ.JHiU@ R d4YP* BVi)JHUeY}'W_]˿Oa% ~/_]KטR\:% ~/_aKq`q_2v2P2@2P2@2PƏ2@֏2P2@2X5>R@|d4Y`* AVi)JHU@ R,l( GVi)JHU@ RdB4 Y@* Va Ͳa?! Az۳y7-.gg+$y-2e+[\oWuSo7+2\doִWݚ(ޞ}j.˺Ħ g5H@j@P@6@@@蔑@Δ@@Ųd:=ʜJ~-k X暳Զ ѷ&H6ᧉ?jG?y`&ojh}+4m'0oπ뺖c#;zVN}{t8|;r ^webX I솎Dڪy;$}Ɲrf4i&Msc,޲9.SϡaeO6pXY z-$#tzt!e͕*yTwACW8F{cy5m苙7Q;i{30yfS1usvj9/Vk:vz$0$l$5کB'sx"L4LLDZ,S5禒haE kٳv/0,>HGӥٗWn(h8[{2}cS~?$O}4^d -<ҩCՏC0 0< {w;Д<|Z֜%׷zm l^,;ejM@7$dEFWϐc6h^(ځسPK9-UǑv? |OP2'N7ΡBU |æ.$ ?]b0Y\Ry?Oڄyy]3:A {;0 =jM]_0H1'v#!& Z'qjlqhy}A0Y}1=X1 lsөaýd\/W9O8vL@Dn5^dSqz] =V?{ /ڊ4c`~`}anOFum708<@Eh m(}]\lǑse94~Qo5[$x%;FK@к8z޳`14A5ur랽纞^ߪ~FjGۚ3@F!F+hKPsJZ1BN ZtDK2b #p9XE3ͱ ڣ#.K h9y<#dr,1:8#s w )pjt/1?K2j,$IJ`7vA$uvwlA[~Qe+ѷ(F(#1g]xP xcg$xG6se3r'aFĢ߅\< 1+ 0DΨ!T{eɟ &A7`d I\S( U,6(0&(S@!vTNw2fDqKA nl/$b+XahYkD[۱!bX g p1Q8$|V@ &`扡^rv6\/䘸&6KLLQOKSD8}CLfZj\ԒB"531:M`HsC]aOobE4PX|f,;L>NқvtȧgBԂS7v嶮HqFmތɓd  Y}"/*{NAIkNđд %Z{!,ׯ߸gw7/_?b,Mhd "3GcCNM 9D=PZ;G Ki+|xFƄE OMwڗe3ˣCU@.+Q &ك'M$"<М|U[(O M11>`xHp_B pmz>p1 w۳9VY$x(01-fW-5|ۊo88rL xk*~0E+>rt$md!!OQe7lBUi. ]%׌KuW/߽\z{oT10,t;Yzn럛CM3NNb`ӏto=0~#(`凴? ~- OuP,~50no_'htX$+0K9:u$ dP*Kw%2iϸpPoF,ƨsEzе |C~Vt^6{~bzL~02[ /T\"]^D^;@pQC32BoLB+bb\رkh7Qɸ Y^`χ7]lUtMepSZf`T؟৚SPPBA`_#x#<\`tT1Fb+1HhXBImY''kuZ}WA/g, Nb !2}Ƕ ~@2 Tm#atCssNU.`1 >? TI @՗±f4ѽ+Ğ9u]dm1#1 ZaTN)T?6gTE@؊ꔽS=kH, !XCkOOh#'B`#O|cb☗NHXf0Ðs&d ߽tvA_!ԜSw{a_hERwL.X)ψ}!rB'VQw@~dK _njT]aGn\>/ACmZ,R|H|,"B÷ACt#B;M^c"sX߆yy0h ӆag9o~yȌfYL*z~X2&a,y؈N t RnD8߬^hW^$qZ$,MAK X*Oɸ )>81 g !xh"sr)D? }82 "(񌣖2#uFSłRq'҇߬-!xBþAhp@=Z88۱aaкH0*SO6)sBGĦ,k1RMfVE92A*I1ZDe(Hx @2>{%Ik5[Wd`dOi?f0UbǙl$ʕЫ*h!f՞A@4%Y1Qpt& <RcBBM`leЊ6x@3)njeYlpӏ]>AݤYQA5rWW^ i+HVL2~#{-EGNŵ;PsBnHn# q=?=ܡwe|zg4*S>zSy뫻WGN 4#ipM\NHΌ> V̘>xP$+P5p N(|ۣI@T)bZpyCtr^"PudN%6H.z%Pb.9Yq$||ϮSa:3me2LY^=b I+arb. љ *BCrhln,mE9{e3jjLt)xIU4 1<7Q2@{ּ7ACPNlR8ޛ≱"g^X)RT /ޅe  晲yv(.2!Lso1#5.o6ΛZRyw\nX>H^̄4v\l j9ރHشS|H7v*_)CDK NJDKcv[2f=-BO-K=9.۽p*M߷Ol:GgN8PJV)6c"E:Lcx]X'ȨGNQAWX%1Mԩ~@* pL{TPO{IY"FVSK^*FMF u&Ȏv$ x}y(HhJfo&_ԼR@.AWTА6`zMDd$l)ny _4 Q0Ǣ;)7ňȀȁT!+|O )Za "m2`_NjJF)%rSe #Rbm@B*!&N'{/ٔ32&f`tQzߗ+0d'j✦:P@H;B^]\L+ϕQctчA2=,A2c|axr<3A0W0.̻ZF⢙hj_-U+AD,D$o gbRF )Í$prztdy9MP0 'بOfh*RmJ=!zu~aⲈj1+ej6ǑPa\JPAIZQN*Ta(BOpF'ީ2?O\R"H恜oTeej: ~2Alm"mH.?t+l]ۊ*_$ `"EuQ[ m?_WfA= Ncu'`v%$n<iKו#L.}XJ4Bn1ТT})@WŜg Ly(82`Th/,$ ' Mx춐yuВ AM(P2hgȢTj^ 'K 5OĽ)lݏFya|qj4/ۉ;L:@ޯ:=RhAǒ*IC C{C 9:E ##cJcbT;q5-m/_Lx?n$@*oX J66ٖ}Cmw]eEyټl +d9*#Ve6QJ6-!]x\~ek+rܣQVO',IHw}K<wM( I^u\Huq(zQ?o.{ `d ys7$2g-=e(Ll_H1 hxlEpf֔4s'@ lE=\4NB!*8T~Jv%$w`5z+!9'E:M6/*Gdf#YI32"Wʡ]Px- N8PݎIdBd16y2V"nHpf^TCe.k7Ō+5 &cʿ Mq5Ju֘KdU-Y; zdae5Ew;uXB]K!{Q(Yzo)v+cl_?y+JHYo 97ANuO၉8]RO&}N":%yVc~ǟ?Upgq^i_jUsбogrZ\۹PBn,-G7 䰛@ąr0b#'Wk.U(Dg%gOt@!*A̗^9 _޿2^&jh^p)\Xo~7F #Rh*ZzD^ v9Z@G5wE_ i6]YK؏JnRDyc7RG5A3u_5:d))02Ȳa&˜px>or{*H^͏wylD@QK`:.Qs;<l%PeKYIaoC[VB20LeRvNTw+4 eez N~+-b#@J&SY8rq.vze4;y] uv'ZxHbʃQ-Ժ_(sku~=kTHFp^`X94 >ED~p)ܺ/.,_{6䅒O,HW4TXܝ||j|}#lp"rv*O"_*ۜWd-(y֜wEpw7{ lzg xUD|+^WDۏ(RW}}2;}3 4pq$We2*,}'!E$sÙz2~ȥnZc}~iAu33l5%kmEΞ8W:݈ `[N`"(;@JOlq0̹U#4;fm lS%0kWuە 96eOyЋ%w9U"""*>+r<4vbU mygV3Ax l knTk>qdw \4W|v.eYРQ*V 멝jisZP3.&qu7 'V]-*F02{}Ot#MWK;E.–t-Ҁ;!UxfӮis.w$BxuǾ` fuقM$%ts4 ~1~ё-Y^s< e-8jw5B8sV+z} WJx-,ƫU;Kz18 RfxQgҿxX"rs\xmu)dq^3wN#IDu$kr"1/n?:T^,US7JQ" Xl~;#Қ"~ 3іY/1Ӧ3/擬t#A)qURjQd΀'CӸj~"/v+(CX"cafb`i.sDTE-( .`.oyl7oGd}mO7i%ZZYd߈'S<"_:iEN aJ1M=' AYtf;)=P\5EIzwOb#^`YxC)^ w)c?޺dQ|èt zVc\4 ˞2XM Oܚ͑X&U$PκV~unPQf'tP Z+PuWu\EEYZz)ʭ2 76ֺ XԪ5nYcv>jj"l'U+?^kvQfT¢NGt) #d˳_=DeWfسQW [͋zX'K7yE\B'b E,"נENEeT#';-y=Xq7Hעm ,.r8k.PlY nAX7He#^ X-oY]vUi1a1%r$8X R F3Y'8\x {w0QX@8띈֧Pu'Q,$RHULۮDz_RLN֬jɎLϛ*i-tXdFLդ_^.UTLJKܿ6Bc$PE13`> #7Akl"hI EPRP5H#|*OU9"$;gÞPa|TBN\HHf*ϭ{5cCl=.AZ~I7ۖأC*5;Z>h@.ZJP Fk5ihec# 25Zad]AN9UU}{='<#jgmPB&{㯞:^V?{LlHgVXa(˞ % # T%MZ)Heuf*&Fk'lc3ಁ bZVH񲠓sM?C<61-rd~$[ *D='&;8w`[d0dVڗHqaTtG`QjsC̐8s4. 3t!i@2kd -*`@x3sIcب2?VH|s%$Geg$ ]6L)ŜVRZLLSsp^q+#ʤrّi@.+Eeat 1ت{g/H(AZ>|\'ɤt ,LY SO"+^;>OYBg;z qKq@':o5|ي >>xV#zsPz'; OHSt OO voED=urTKLnĐYQn{1'H#%3W *lR{h I< /?;UP8^TH]ܟlFLC="z/ɡ[zw"v"a#PNoZr6@p(C&vdIRp,Q]bFlyj鞒7e!r\2I/J<}oNzh9+X)B$^xcN՞kQaS3R#ep_alU7d5&vH2\ڈdRm0 %XH9:uCGE4ԝlt*VZD7nJg v4!L5( A HW4d\: $FQ1{{Hvj$Ax̓5nW1;Q򼾲m}4;6/h.StsheVwe9}0G ƵQEb = d|2l6:絋VW9@oIxr1t奍еPE9 Pe"\Q\5x`gP{ $8ٮC9t= ekO{补ɦzQ+ M.x 2>5=vuRɝ19QYY|OzΈݦ1>d1vtճEڌ}N7?0o5EqR?D2=|lqoW%p*IlxEVd4 JO!sYSV G?9lkl{H\FQeOEQ-&Q4_ h@|&.1ZexQDȕ0PeTPYa{WLIdk?F3G:1G0ƌV7TS+&Fnur)ZSeyQk'L݃Bd;[gaZ2`Q|.@'ra(2o9|.«"ڰ`;PǧQsċ7 |/*"v/vjNY FMH<55xy#`h/^ QAc 7`lz%V$j kPaTKR,Gcu tfVNvL!zl~*B[%.Z rW% ^P2 GvT5<"KI=S//؋Iᜨ~t]Z{f1J009ʸ+ i#H՟U̷MZM_Qku&RG ա\Vf0UUf Em9٥mLR6*+93#Z6g|?n:ih`.Cw4=LHԬ+=h7^x]Ii$Ӕ$rxCۍh4I9ϊ%UZWVrX R%~`aъ6/̏>@?f^4:#F)Y8Bvdx)W:m 1|l V_12 TB"NF|$[B8[ƎIYR$8" K=4XGӅC]Z&QSN>toeIfn G*1RnPU_(^FP1Oo-?D/̨9'`~-jN,Yd^! 6)eKkڎR@@=UVi%ND.JFT#Go$iWq]]:fFuX6Xg)8o$\% QBEڥ} T|b4Lݍ PЎEy y0Ȓt{N㳨_boNEF?- @钥?c"p($iJmb}a:PݶRr3\'is(}.ViCۛwLmcK``-޿<.=܇5U zBVY_4UR򯗒XL:Av8PsL'r<[,![l3$i9KZo~+V"*ŹP/4䛬2ߛθ|<-.̔uE͉# vG ]"h=,VkQ.h0NMPh_xt@:CoOtb8b57 YΌnF qk7P6yz0:56o ITU*;Π]i(:R4)/̓mC$8Ef1i{R|d5 35n7~TuW'հQ]R[{?,=o$O({@ضzNRI6^!XDBzu|RV\Xt pl>cf- ;an|gȜ_bM Lg/fAl(.٦ޫ۹djԳ<$.yt$\5JwЏ^Nj}2m-f{pf:CgwElSe`[N3Zv?"nvT #د9,D>bNs!äO*|Oc7K75]' }c"oz+>cv3l}^ft+3%/ѦDz3l }PKE#E RbuPKP< layout-cache5!@PAM#,)nڷȨ$E ԠfC(E'o~X8T݋PK*]%\PKP<=<1 manifest.rdfn0EwrfYdhjCP}]ҢUչ:8<{Όl[7?QF=O,-p*Q,Tk!ɲ C"[2b,#V`_A":q4v!Zl2 Kc~|' *S "tr5M }j"'.#*u15teJ]a2zPKP< styles.xml][۶~QNTBzi}8qN۷$Jn$x(Qd9``0\ͷm<`$ӑca}{oj& ~1sSԁqz#7az-NoCB7&hJ~fi̶0-EKY:hg[f-O#wE\lŠ(ߎ6%7ncBדDPs/h$#K'ly![8)RmZCi5}X[[úmz災zYvئE' miXwSr !B\o:ȿ ]' SdQ爓mh7jn;ZP wP FËS5A#+3PCwBj(/hx^hQTp(Sd0hNRXHrEŲ|``5?Fwi8`?Jム-ȿ9${vIW046+Ģ>rR ػNlB 54Gp֟V jGȜ{PĤU*)U kc8ؒӸĒW 88[nUXh2Kv`( 4z@L`8>qwd ꐽwJDCN:V0Z-L8Vh.LE 2p$Y{ ^gO*,m\5? oO {̗4i !w ԲxH-uJ5E&GW&𧰘Kt:߹P?Dv4}X!Bp\n n ["-:~CZk u*X".dW._W(J +HE!I.mi&EK, S*gs.5oGuٲd`@|ovF e0h$)vsv.w7YkU$"pd4yxEDi$y "3Od1`w3X!. ^:]QkVUa)Mz}|}K9uVIpƓ u@yL68׍P+d s--beoAjCre-eMbaWڢsoS[Qy9y6s++O'0bưUãxX,y5$K7F.,¦ߒP> ӉPrS>a]qR1N\F֘mxC 8@4z\Q4R-EDx|zQ`8VCfvs)Mw7oے]k[Da /<1]ߗ1|)Еc1hCΩɚ(qWb&5gO"h(1ɧaiR< aI1 YU_?-K>&cURF!(b@^z:ڌC0I9͕/(" Һ+%x ` *( ;d#tՎ_úc?CQ_&J>f^Tq8cVs=H(~Ѭk=`׳˯ pItIݬE(Kirs\[J 1J=t|b=]59ʺmv)Y??J) m)}*x#UA+蜰Hѝ%XtrŭT*jjH멏%`L!΋V$+k)=Lع}5c"#FRW?4fWE1 5]B k,8#h r#8*"m/Ɨgl / kk?_ieUc>Hݜ=>89R?X~z0?z}x~?:z홸Zl )3 s 17 o윮U{/׽ǨcuE:W$ 37QiE%$2X֣%=Jѳ~Cڧ~ޕ1)6'8|S/>%duH76Ǣe|w#;dכ_SLuȰp>6d)|-x0+59\c藌kMcFhUԘ>qn 'd`8l̺2%<%qb:q/N2Gws/>&57:wg㞩_qi"-%8d):K\NM _AOh3ExӪ[3]f#p>g~a&@,CjVo)a;hq16@C[ٖf{1rEGח-M|`p=GZ$C; D)}j=6'R%Ȟ_!ݰDgs<ֿ AZ@9c^RPTyJJ%MڅI9sN–__=7E_s+|I\,VWSg1\ߗ?$'QYG~$ ]F%Mq3@VoDLMJ)b>cC&JذXٜɆ:m[,eu6@(=]t*ce݀X"1c{FO؄ͻ='UF8SvEGf¨-]yK\8܆AՁVq`u@@4zk;mT ƅZբ}t?X 8b1mH}uYRFFqPus~kU)@P+s}65~^5Q6 Ǘ"39k¢W$b(3GW/DuAX$h4PpԈ ѓM9󧪂x:{ѨR>gW 柰  䄊ф' wтsA- gG3CjтtA - gGg7Λ'PRBZΎgh'MJiI8;ҋ f7m*8_>`nùT#=~VPӋФքS.Lyf0 UTv5'jrp$_KۜMY27m-RNN6Eaeo+ˏZX}2Vӿǁy0lPW/%hhƄaNN<,m:qR60En-j+yq ݷG?uüIGt^Oxfp?Rlm 7v`i |*M J{3J w=j5i g/PZj1ijY|\wVո )ևTt(ѯKK~hOtyЊ?ĂnYrt[FylVƸrUzܧiuY妥+mG 2y+c\8UVorf@Ά+qQSUR/rɊ:gF!)@b*>Ȫ!j{ C"}'Wޛ%Oލ'3*?yWɂΙ8F%wV巚ZIMo+4PK(7qPKP Matti KP20NG2009-08-26T20:55:512010-02-16T02:53:48Matti KP20NGPT473H06M32S998OpenOffice.org/3.2$Unix OpenOffice.org_project/320m12$Build-9483Matti KP20NG2009-10-21T22:55:42PKP<Thumbnails/thumbnail.pngWi4߶XF$=K%D023E1"Ɩ,ٗ٥k)kve2!3c>9y9W^u$Yr FppE蔦20pZulvhr=m0n^භ]mVm4!m߮pr||AɀĖ:K='l Wwvv677s].fB"{yKFVb˭ y36|ybt̵;Р>lCZRv,u[jbGKH?Ҷ_UZC9rڟNA@HLXD%Al}"+l A<=&n[26X\kJO3r y_ޓwپ5'vbe_2쒴@%ya8_(_"&A:3:Q\}c$rȒ7k7OYAR?mP,GZ?v2>,t$ec/UG4_}u^ b"zꞌ\\ّ-v-?Xʞo \k]g;J9`Oacߢ{Pi/1K6{C T#ƸU vR vݍ\ff%V%#?jh2"FO wTv$9/9o$63:u3:\TwwsŒs֨&vZާg4^s"*F]g֗f =j4e›3.xRvUlZj+mhD:xҚ'UZgvm~?ۊn`އ͒OKPgsbAkc-;^!`5_`AVl{'O}g1 =*(5W$^@ju**dg+@<,I6 H!~YQ2jrPz̏h܀CXGOWI]E`ibVpwxLTh)Jc-04ؓzѭRuc&/+K*5+8cD&Oy8rwY9K*_K 4F g.M:dߞT ̥K/w(̪6BH,|OU nQ3+ +`OdH"REDר iRxذic@l!/<7ͤs}D9'cq} U`I5+o…#ұ%/wT=dX+G_0VjJ(EwjNv{Cp)RX#n;CRٳlqf .ǘ+* ~*.w}SVv<3E{u(- MӗhA2|w?xb,Fcb{k4v>WߘT1pjl@MMA7scL~bPo(Fß`2wEHNv,%}|1}!Er*|䇾l]o%"377&z >ʻp$1)mRV]\)eV/XYHokV;r[P;m ;.P!_(1ɉ\|^&KRtsDWŢJ 4G5BdHǍ0ϔEkسXMo)Њ 8 ¨#`=F=JrW h l{+ bGW݄,{Kn%Hk#& 3*&ڨ?O*%  T[Imp muif1/4??Stc&osUŹ*9 rrHw:[O.l`⬁ݐ4YCrh[dԅ6+m6F?0@[ًA$uGϘ`,z":]OhWoa2 ~0,WouOLsJ,l6~^柋Y̌^(E>* O+r ޢdٽ2Fv><3z{6ӫa#I*V>WBvLcW <9Α}JJs) E%ǯW0V]pc+s׿tXʤiPd"k^Lgsy%sAF-B00 ,7RuJ%ܪNyݽ=RziK'=n> >t+"or AN;!y+&9^5PKc PKP<'Configurations2/accelerator/current.xmlPKP<Configurations2/progressbar/PKP<Configurations2/floater/PKP<Configurations2/popupmenu/PKP<Configurations2/menubar/PKP<Configurations2/toolbar/PKP<Configurations2/images/Bitmaps/PKP<Configurations2/statusbar/PKP< settings.xmlZ[WH~_u"Q4 rGy$dtg;ouezYtWzBdξ2'\lzӯo?dPԩp<Ǚ,o2`%N/K K-cϫKʜ&3S*,eqϿp1͞lr}ğ*]YC~ 5&Q {di'3w?o|T~ 훓emMU|??zK0~G-BC9fuv]0Q(w{jQp0Og f<aS+))+~:"x,{MPӀ>`޺6gY ևXbT ?[sxZ/W煽SOK"L'K 6[~yUz1Aef݌ u05ɂGi^J+bWqܞPpx {gv{ \`ܽ"Avd-JyF'$a @E~4~O :`k:@Cmb=fIv\%4.hfh1Ҁڂ+VU=M$ڃ#jI9 %T9bFAl,/"PLy43s6 ~ґe=Z;˚zAKϚWYeAFs[ !n Afxc2h^4쿼V #67o-Xjzsqm{i{޷yL\~Zݷr#6,$uU<%;s :֠fB0;.% z؇(`o1HlDUmaq&}Dj$HĞmnr+ b Ahۏzlm"HGWSPVx q/BT:%_lx?{<Z#9x] gtaFR=9~Ow[AX⓶P%h-i@57"vTnYgM_hD}Xribg\q!^3WAٵ^d$7PK"PKP<META-INF/manifest.xmlN }[S]fM|g#@బo/]VuW; ;l>(kJXFJdoWw1Pqjdi nɢ7BaP,CSY[4T|/z.k1O.29,8JyŇ Nx v=j1ֹڕL0qS@BӤ5ԧֶU|wݠr rPOQ.ONc:c[$b5ttjrg^UqSB1u2z?,Em<@8WEޤ8'϶0d Zh0gE-cv 8P~B4Qq9t-if$Ji^oPK'dh(PKP<^2 ''mimetypePKPCxV^} qUTTPnݺ۷o/,,|CFYn[|}կ__~5j;s%PЫ`u,/^AA6&O?oE6s̷~9rA6N ĬYeʟ_~M4 lo'[n޽;@̐YfW{ԩSЂx7>H?_}ղe."[n9sз]vC=ġNJg^ul}{8ψ= ތ}sZ =;$26T,C8¾+Wݗ^z)J'% 3QFr53!C3X;aȎ&LO*P< `]vܹv8(+`RTT@]v XΥKKff&oI?8 y͘1+0syaÆl 6Rew)0{`3"ħL.B2i@\Q"7pmR&:Sa0q̘1O<3g+Bz'/2ԡ{6 ֠A: Ng}6K *~ؙ%d4ᬑ"EWH3zTHظmCk,O U!s.DJ1w޼J?N8 &:Y ~kLQ ׮];Q5TT= JF O-d/e5G U}aÆl 7x#!d O>.w  >`]JhѴ`\yHFb"hr 7ӧLjZ ]A;ka=sXXL}r(#00_c6c-l/4"ϡ hLtEvLRŏzJbg%.Y;?! lڴqdNiJ l{ ؅,L/-A\ F F%=vog)ˊ#dO6؅4NRAbI"Qv$?STј]23StGHRŏpS vˈ FSIt-)2Gşd $Q*fƝS+w-Y*g`-2A?6Uo}1B^ gd Ν;c5bBnCߑ*~j% JL'?_/^o`XIN1}]u:u4wܱcTDZN+{.\EO *,ФI 99Oɡ&'hȑO GDѣeӦMq,UQQA W4Hf'^>`F g6ӂtî9}?ڵkGD)$b|N->iժ5 rm۶[n CDB"$p3Tvhܹ3G'؅ %CSvg/ޢ3$Ditðy׾902QKJx6A?[{$ѩS8}y8i J|؎b ڱGytrV ёai8-!L}Li~o-[ vڌߎ'LaeN:c ( ޠA͛@2H3g7nP(Ά [8A-ZA#eeei{ɒ%E#, sϞ=9ի9z~/^ܽ{w䕾2eu͊0f{yZPyGTٴi ؍Qf+Nt <3$yyyX.4:vuʕ?H,frLz`'䈲p^zUeZtR*CR qPrC\WN~peeeb`BNF񢲲.bO0Biě} ***c Bj8ɓyQXTTD9 n6~l>%Z)%,a{ec_GTU|BAؤJtŸv34?Ǐ$ֲ)'m_) Oq1#UտX~"i,L:VXRHrNTma6I#oWxkϞ=[lҥ fIFF޽{k׮F߿?33sݍ7|;w\jUǎ7oܣG诤H~ 6,Znݺ:tXfM˖-uF-[֦M⊊˗Ȏ;NHuVaѣG7oޜ:bŊ2+))2zɏ$''AdvQQAEɃ:۷oժՔ)SLR ̙36lx(%^T@! ֺuW{%??v5nzĉ6֯_pP4'L@ضm[vv6)a (TF#@al*,,dǂDiڴ)m/%H~D06CIwxŕ0{WLՂ%UZݭ~&M(,h{uAq DyŶA0Sp (~KÐǟ}peShC~!<C~!<C~!<C~!<C~!<V8yIENDB`PKaxD layout-cache]?Hai2!,!H %!%hh­ jhT  I-AAZ5QM=}ՂR=Ӏ6М2@ v .sB۰*syNisYr]Igk& [&u6b8n_&&hZ46kXf܁v]U0^SisŁO'S|79$o0ijxwSxE z9ށ];ʊndz>~خ`U/|7EE Oxߠ4\SׇWG_G__X""g\=v4^H_lem_v_9^Yޖ22LGW[AfY\`xl=|&Wa8+zwEJ_Ab1z))7~`WNal|pCK$dk8rK4)ήD )0WJxJ~o⯟'NXs9|{⎠X Z_YG8DO x!u.?«%`=-ſpYq?^ q1BK9d0^ȟ^DOg;YNk55#Cg#0_LƲNj/fZA9owRe܊EkiTʯEUJ[Wsߵ֨5ECTXQlVTwV#JYOL5Je'+NRoru&>Ku\|"ͫ:Yʴcd( FrJF8(4|7#|@d'lNARy#Yjܬhl.C躏zQ~&r`4nUՒӕ *.jN !HSn9hn 1+PEjV+ћE n՜1t9܋VC]|iA$ pSwc8UH2ݮas~<#JMa¥L gG bxȎN}N_3/y ʗA<ʋw,6yl樧❧ W)zredDmB1?a2,ebTE.*dXz)|4Unt㜋!M^(x]k)tIM' ".*l9X'N!%=OjE_Ne[w{5|RxSKN*dhlv2O^iT/ls>BEs>鯨ΜwHV$;ޛbƩdו]_-J9Klϖl\i˙m̏FsNg͔XMJ5q}k٫37_~Js]L뮫 TB}..5ُ̗fFhnu cxuWcZS/p?A!wuuY\ʀ1o2f03L3og. ; 3R)7SgHe3XX0'_0ޒ3`#{rvRWnT΢I}q r2ߒ36̠>| l,uZP1KsEfg=@uLz&>8fIf'Q9dfUQ9dK+< @y'LCZgrsSesh9H0s˜wfp3ٜc3oL[69ˑLA*(gڪf8z`y0(*s#/4E#Z$?/lv0SIw+n"N~jiS8R5O P?mvT]6TdDUpݩd?7gK?`ZcڳXhs=A7j#?(4ƀJ@yӌr%kҙlnR&j*de7s20#cH<x.~%6҂>ŏykCh)\ކMroWtM\>}ϯ"Owym¨n g5osWT%Pt8<3ͫ)4TDQsMsƚyj;f+Z^'T -WEV=%.c.-c+SC4?NA1VGkN@P;ϐ?^Hhv”+e%)7rc-\))Hu Y " C&hp>56r5t̬ g`,G9ZB.e~zl[(ӢS $ 3X{̋=@!ܵUh^vȟ{fUat!DwN I<ܯoayg!Mdb;FRz(P\5^a=䔚;̫:}ېyYi@nw+!ڭҜ/< WNk%W(vOÕӃέGv$a8= 1F5JOE(Yyu^i'Z &TQ#J09 AJma{WP0#-'Zi"7SUAq˺do5?'ʓ!lSg~r^hRԳ] 태Zgv. ApO2VP0"pT=k&&̼F](x>s.ww)LVL@KeɄ%%*8'.v_EU_@/=RD)6Uw.;>pԪ\,W,)C{+M&Rr d7˕.9A᪹P/ӉU2RG)r wTPJ)Ȝ!ͻ.rQ.4'V \KUo\`)TDOs8ru3g&w T%AQ@v0|9YoaTfNUizoKߢ2.s[~'I\S琑z5\5\iu\)m{|5Θ ނsnO?uvG}u=EV6|nên(~Ue*(G (Pr}w+ʼo,|~NmeEjiR2!RR]oNn˙jrmTn J;%rVͩV3<'[9QEp`Y%fɑ:9#1kVڨxTxHB#0ʢw7eQz4lxXa'yXL{+k`͏|_ӬNHxX<1~(X9]dr`5>~Enu";b2xYL޵Xwux*ejYWnmXWˮXWm `eU{힁G#YK2W{xcGɜqmfBͶ=*E/ȩأjR=%޺ޯ ڣb{=HدnlPўM'*cjSd=ǯ*xQ́x^ސPU{4LSe2G+xQw`^XViU{lbwgzWQ12ؓ9j \dzăwrѵμ(T񳀹)ru?^l=y^j nVgy C"V%~ &`K=w7oԑ-~s$jvke,AԱ ]vfw# f{ v"k>ƴYy!?{ø/Xk~F'FDPIԑIMfܾ;YY;LOq58P0Us%hrr4 wz! 6SɛC"ڦ́ "ѹL\4DJ_:͐]q0VVpx$˜1<= ^b^HY⋳qkqKrJWÎWW?QYqkq+ִo1p6^;^I^ Β_[[[#?Ublvy5j;K~q6n:nIn}5f+ 1K0k֟*rK|q*fztΫh:ԣ=PVqC=Zëh:ԣEPqC=ĬPW"nuG{xաVzW&nuGxա-VzYmA=ѡ;:ԣEPC=Zŭh :ԣMPC=Zĭhڂz( Rܥwŭx?[sp \7q˕#|B2Z˗sc\,{v~tB/Yw/΍C(s% eed!߽;7̕,lỗaG"|Rx2W2^QD]!cT<&AVǡC bG){Ϧs  l [!aNSs<̌I^:{{ah" _vb D4ɳ1KYtn6Qޫy1Nt<‰0t} slM횹lŢQ|{"ѻQOfzMj$i)Ι.d&Ǚ5s#b?#?k:f48Mĩ$g "! 68j3k3{37jv3ay)8\f`Xc{5 rh/̨I\a{پߵiGxk` 0<8' 8MnDSTTO?Ys99'\0QM6g˻ɶU:A3əf߬, -tse]=A؍Q|<8^'r?Ӥ[i<S94ZK`kaDC0DHnCoљuY~(tt-Dz81(/}Y;wV '!59u6^)wzcQpdNJ'l"'J'<~ 3R(XE5vv-"ƹuglkq F O2<=&j'@( 01Z'g y`<c0WF6ys?罂񴦹O~}FHM;1.7{5J: G4u"GZu3jq3 (#v29o+̨qǟf\c 2T1gˈBPFw۫@y'_'o߰cyW7^Y9}lf-V*|} G=@DcUC>mH5R$#qAB.{gO32@}`M0֪@#{;UC^ =Ȏ5Cr D fož bOjs*0m޵bp8vh x#qI!v/y\a/)k9&AwlP` NaO?a-Bjk'pzr<Fƞ7`H$gyg7S=cY=yy?]}{hkf6$2a]od#Ky}aH}ؼc2Cc4@INY~.+m{ x"/gƺz*nyJSַ|j~@#ߎ ދk ¬9sAZ  X9ٵO1" W-U68^ gN˺YbݼHl6ϯnU+}?o߾]puK*MHU=>3L߸WDOVg\mũ+/,J 2 Qb`bɷ] .Yd4 Gh0YS>dV9/^ۄZ ӲvS7^De?ЖO8Ϝl͙ MM2難,[`{ >'dG#حۏ߯GwܬwgV~(K͡S(}Ԃfޟ\k+kq B'j_Qv b7d-z Wj_c'4["It qS!~N} XpI]6[?(E ZŝK k0+PXc Odũc @-1`/t!//Al>$>Zq.vxȁ'z rY${Ă R N0Fݺ@`9 @߶wa ¢Mj7?@14P5pVOj _݈V v,7UY<۹`)\Cw\'wEzцԨVd<-M@Wye_dh5hGV&'"tq8^G%š V*A~2ͷ!6m=2W6SawoaKc`r` Zxw|[$/tOcP5>;h7 ́>|g{7LkȺ 2Ж?Y4l/|A2#ՑC'`s~qo? @ȟhK8atᲙ&\B>n(p ,etV$(>p" A3 nQJ.[+_($JQ)`<M̊~B[)wŋhh%K)Un+%e?D=D=41^Uڡ Bb0UbG}~eWq{,y: 6 >2s\yM}6[,Q#`C29cU'(DQ /6A.f=6hU郩dffpBfN*4R3SOc;9H*T͋Ӆ1 +*VP!Le|ҕd 񣙒OBWk ֙Ð E5)a>jĠ$^0/Ƀxౌx!|cS|sg'—e QhǗwCp{\4xkư#)A :=[ysC%;)^veFtBk+~E ܱc@IH'6i3\˘p:ΦLkك :.6P*tvй$.R#q3y.lAx|_gXyjkk~N59fqpzc֪ؑR"RiO%f\N= zϮ񗯯sf6L*b^ߒ+1QPG]4,wo ؖmQX# ;Aq΍ǕmpѧU>?KiTnL_=sb&ڄQ}B瀂OIן t+'uNu[X4[R9/cI}&J?w> /I-75̙:]94 70RI}.-%L%x"mΡ TAbj4,/QI U7&j:K>p>)Z?Ð{>\Q&h5;dr˺-3uiToψmY':wI[nѠrecPɬ-aL&@bݨ ;Hϥi P(}Zh hȎ0ab}c2 Lϱ10f/O2( 3 IAh@ -^\$ǺM.p0L}2<dP 8@{KRD';} Rh7;@I/+8$.n䓧Gc3/m R>]{p"%u!CyH_z=`s BUS=2ͬ:!&NITr \pUꔡRl "3:y H[ qzڸ/~(0jE A0r B0roU|#tEcF*yd{  (D_PuxH28tn6č(Tq,RALh%HP%|jgY U B70nXG껶D-d0𓭄\#9/5HHJ C_uxF۠TѱMuwd_r"LvD6Cct0 >WV ܃>{ZwgyU Rq_N"-8(/范2? ;c_v ר(*df0'{ 6V6;>O'+m0Ѷr*7~{g0G*5Cɥ\ϵSQ=rRf=OH\$a|Rd|촍$uGGFVޑ>Y9ǟ {0Td= "0WT veV|x{[%+ԝWu~hZpU>A1&y@<9Q-J܏nakPn?]O4X+@ ا>j"QާlpHt,૓vpYQx#!Aa&3ly x"ɜSnH>+ȑ?p곟zsL8,R;iÊSE+ 6$Z<`(b[os6+([47Yȧ+W)Zk`LWX%c`7DwaQc"GJ@8'x0 1˖⪛&/U)xO6+fƣ L+`7 O0 dF yQR1@tF`/ЃcϏi)NHdzlf{7KO ʋWɋfx xVoM)_4r'.jnPې?T 'W``{tQ9XFv{7VաLZ_of:3CR(uwҼl2kB>z$52H&)Rp:1H.Ms $,$t(Tg 5elJG(nn55΃4r1{jL`ͦ3c8FlEd,mQ: f82k5f.7`!I/`X ^fKNWƵ0 ް)W%;?,UI5{x~zIR=wmdTѵMa,޸zv>E dP}4"m=vI$(';J]kte0OX&QkpymUW*zhEm*0/}.Mr:L>mBɻ"R" 5jD?)2>xZ@RUrfdƝ[<ٜ8MRaɱ2S7g AвIڛ*qѩ;X,QՠaZN);qUR_78,EV9sqާfLYoZ?*w JbE솃.*el RMʲh쟩,wty<NX9Nn87KVe픭7 BQ s=Ygi@whcr}6trv}bQo7 TBleB"Cy>"4K!/&0ȿ D@˖!Zo;r#`BVQ-\!`- PO߾Ur'BAFM\Ɍa/!ogfe/l"̃U?o4Ao "|7!ϘF?}ZєdRM$˥w]aB +xtM DQasWX5A֑y">r!~?TV["dv?ڨHG`#j ?A|iטXUCl34 ͕\@`2%ifN`2OñLy;8q݋땸043KFꨕQc|/c>t N[s?"+j>G{L$py:/gTIL9/6 oZ ʞ:e:L+Y-h0܋J$pao2]H$Ʒ)s+2~i'} i+j,~Rgtg!YG'8rȕ2i괕ʵ{O_{ssʭ [a|Hw}ݷ{P\`' D8t5LX *p`P)K3mEuwir(dqYM..XCy-1D /d@m*&4խRhSZb#剺g|dcj4O3㵡 a#4QzQ-g@8!3꬛XW#,TosAG,^({ջcCַ㬏 s`6T{߶4ii1F;0]FK~9([">ޕ> [~];a1-~' A+@ژOjUteѳ4#蓉120;T,A>~!|+'}5:[i>Bg90RYڳٟMP] 4z{ٟ?3XkQ:;]ґc#ƨ)C;{6kcr 7+VB;Jmx;g򚨐u~Ԝ_E@_Ld2S RNzX9D F9.=ܳeTryTշaA$_a y-eܜ.#[m&ѥ&e^jA/X^}!xaZtѢHLU6o@1"]8d}:rߎ_߻p+gѾU@1*!eHECSRKveV jfkw9B\j`/9+_yzm 3''8 a΋NC(ڌ&So VXZيJ?՝^#v<]0xF&Ŷ" !<| b̠pƠZxBz 7Nx+ DhኯlYf (G"}6ߜ(l7*+T~**9 KArii6eWe!AE3Mr,D"F3 UbRz>{u87 ճbA( %W(@ `zQ"SL]Q|)[P tDɆ.JYph>l:Lj dXǪ7 C0CF+흨lh,cY=~"s?̣?S~f)4Va?jD#2PU*6V)thބyENƬ0N.3D~/~L,LZV,)DhJ7otc}0fTGu0$qC{f=#Ǻ[$xl.Rk"uvu]~a+?b2|}h.С:ُ=TCZ@uw"_SDx6L1.ZZK[{΂r# m~HxMX@X"sÓ6` jPJDdJݪӑsl2\%-aM  }7x`u= ׊&xr\HPR$#l@ jh%6#R`v()DL6X] jmk"XcP9voF ]`+?y$QE.XŠ|9c?j lfiRf0rTcb7Wᑠ-|y1xƒhZ2`3 Q!&3Qu9 ;[eѧ]k.bP-#A^¤ZM~P̾>C+f7I@8I\[(=PySH\oÓP9iQb 5 $1'5ʬAq7#1Bف0J#\?y%u/W@ HtRB tWCyTsH T%,Ưku&niפqK d>Uzʸ{¾t14:SN˜>2oנ,VArQtE e$o˲wމL)Msn;L/S0W("a/o?]B569'K?,p8qan[mQ⇾>ʕ}[9[&;Ez&[rz $0*%=VF7QFm^lw˩ s.MHB=Ui*5Ja5^u[h5Ɠ$~[RȾNSg`ѽn',"hțO׍l )`G0GeUuCr6^;1c"d25x7.E`o0 «?ZNإXwmlK  t8csg3+1T'9(PpAK'D QR rTZ/ZR)j}9*a>˧JC ,Q)хE h^O3SmgzqR O=e׳͓8x~ɝFlh M:7VGp3m o;y6X x#09NMy ćP_=,4='g_~{˜nL4.E;^ľfdYiW (HYb_j$bLTW[蒆.KnF8~Jގ>H8{b<^Wv*[3sX}stBqvDٻ _3R+N4sa[< mZ|\tsix9Iyyɥ.|\o)mrF2He;T %3WJŷߝu)[);\1䏧("'۝3m,H/^`Ż̺4߸dd=Vqy@BQf u^g'Qd;"Thv3 J. lQV{$':o H! 2\bIcHX焕F%#1pUQS(踫{.;kɨgm6j :߷.0q,V>=F&=̱9fctz{×\7DIӆ獰1n#lD|ԅ@g/a#RbxEBm)pU0^ؿ3Ƹ|L7U8h`_~xo_^~{o \EU`mF*u.9aYjzbwVG9kO1:$s| cjn 5+ z&g=YOMBMx;5X#%EsZ9)WYOўu=4ۖ2.3TE cʹm %0v^(rdh;69rb) 0Nȑ0]W 2E3$cd!e3*;sF3Q Z`)nwmEDY_0D5U~d%G:d0S lmnmli\3L]Ԍ)oD=Lh4F9c>5=ρ?ǻ7$z<] y~rt`5sL/oG´v:σw~2h24sH73KF+tU\R- [SJ!1O}0鋆;:Χ=_qDu#}Bt$ Ibj9;B`Kbkox!tjLȜ ŋa_fKvv5'YyB/wH&f38A |l.(FE_Q]GY=a#j>L#L\ m6)-*wp!7R+sD  f-ȫP1Y;i;z+Qۋ桭ŗ0y#6jC7d /#=[Q:RMsh5B϶;'Q$?P~ ŗS 7*ȯ`9ıCr_w Jsl3"q3r`dl'02%o9ny/o8[eo^D1 !n$6@D* j^rм~&P¥)-{T3v`0&>ܻޜ:JәY4~Rɠ٢bx~էu"~ˡi`q[w7Xsxs!%e&煂IĨ!%<-1J| -È|t"svoIk>vSX YPHYs?ڡ:c0dhLM(Z?,Ra gt4a$ (+$U- mYTrr%o9+r][.β02v<(4Ŭ۩ϸhЀeSM66~yl47g* Jo,c*5XEjOuRj3#o;S3:S`M eAf$$d_zF۰}2䂘H(x#--AXSFP' ۋD_i7νUxlq4Sc;Tԑ9y*;= zqpH`Y@>s"ϳ)PVM~E?NE.uE5+S#~E(,.q S]ѷ)+'1Jd-GJ:o`x,C?bЊ"kqKQؘO,u +g*]$0%שlYJϩ!2Ice9i"ճ*cs86G'B.WGXt&,XQY/¨"/ O K/{~s=˶g%+kj,Fe8@[ýqN_^9d9Ǹse p1_EQEdP&T,WЙ&R%Q<ŵXcԾc:m(&}jxq&7x{m';HF)EXbȲVA>Y\»ߢ-އCXE8&2sT5l;s̾xMUZ_.$~ 2wC a3]& PX .~CmĖBn-HO_|(b rv8ѓŴj!u5tIdI$XsD'xiB'p~Çrx!啮Y(e*;e clzӽ_ZUղkoQfoWv'|wn{f;9>vÙWT-P;D=ހ8dU/1`2Uh2ӧx{`gSTuLǓ鯦1t}8S <4OD2y7_q;ЁoQ& n#3R8QпWR6/@K_*d=9a=T*f%nf+%E}u`x?m,8=g®,ŠC-5dduHݶDS6>~fO?&;@:><<$wưN$TJ:}F\a@6r.]fk/3J&O{˓B)ts88uvYe[D3]߳@U0gL 2S|gRJ޽o@<}-{N|&~ -b8)CyqIK1 4:ߠʿca !t xCJLʑ/Ȼ$PlTz܏n3<顇C 8AG"^@zZ ,mAl 'ӶBFE1Q6q #ƴ{fQBBuNH-<3 GB70m)Rb,0Α+rX\|lʂap 1%0d1;T񋍱[@5'1OMc0g9>$KgarǨ28eUZx)OwFniT?xZQSζ8yEEl,FHi3qDBQ `_Y xL#4uyW4Y=-/ȓ>-\+ gߏG_M>(ōL1=0 ڍɿEwM;K0,p#g7~XQfG =3XIJe9JgËt lcS-`$b1"-1|)vr ' ί. m$X~ RY&3%_-؎^t{$8rO/;n:;0l~FfPk 5Op6h83}:''jqJ>$~;B~,pj!aԶ!rG T]t )4xPM{`PЮ}쇷..Èn~E8X‰+fk#J|.\xeRXJKG)H'DYVz%NTb_XOT$J!F` " *Q8 |1 FzfD[e'a#p #$\BkGQr3t,3R1qU6:YJ&.{ԗ5Yl*,/]x֡шxt("#IZPND ڄ3Jij6RsyKL=v㌺O;L([I}Z8qXܝQ|LbeFj͹=}C 1tEC֨u܌sZܢ"L߷m,3~}̇%(gG$OcLZ{q} af짪fxZ`ֽFXƓօfJ+y{C^7 ^i)C i_幘`:MD_Xvmض> B*la^TeJeoq R{Bca#ŏv̑fʳ9..JӇhcƪ}B3^Yzt>澫dAIH!H;(BIW|&VZ%zTMTGɍTi| =18K-Z]fZ^*y)6f; ((!Bd!ms#x,Jis0HBL,rAu@A42_'[da¸,t\Act"^%]?YX0FIOvj(NqQ2 o< 뤰OgYDlJdEqX N$ԫOJgyF>р%?Sodn'MS&ҍԌ#@e!/#9|#L eo+id܈\ 9=?(|!:UVڃ..Srj?{3Lh*S{hM$BxVXp865\X.\n44*XbXlp8;|]9VsƲhJc;^ĢME/?Il.0 }4q`4 /:Qa5 EۮG 'ɰw⼸UǬ;q$XzjvrE!m=\3P@ZA(hȗN)'V3u[ @ٚPY x6|2ˢ~?^|h2=1nrሚ"ؓDMc,`(*fJ&FDZe"/N$5S^JGFU4^O[<ުP垖kMA|n|6}UFɁv"DpHNV:"$\YhQpq)5f܄h5,Wjy!cU\wĿ_4XL=''~C*M5@%*9?QdBf ?_@Sv׺Lg2X~a潬bwdDMN1e2."y~grt/ ~cnﴝf~7auL:skC}N *OFϟ(f% ! hV&2zgkZj4$9ݟͦbܞUs{vv"eմˏi]/iNsNzu{v+UMz?^ `d98Ĩ  gC)N*Z.n#.0?'" i.?lW$ ;DH+d0kFKMX(vODÐZ21RvN<[@&_>R0>dp^jeO42ƷI4SaR% =}/ƀ${M3 hLNxe1T9xqk*m1԰w;+es2!:v@j8du좂zWLRPH @SrYʢW^\*y qaGN0[h99*.d,J-~n x8񢨢b׹0\-ZL~ed]9BzXr BHxDŷ1Ʒma -߅t$!=i*l]e%YQ3wTG6Y#  bcp_YS%]l6%xj<#DȒҡ9n h؏ x6.2j~}~Pco4 Q[y %c73\sļCs0הkȔ%rF,3I'̯bVzR21Y쎷u@S"a̢hPˢ\(Q0Js' hK'8%\UuR0Rp `24*$]#ɜx,R\53S{wLSizWC[q9K?`w>nx$>%S]۬'hW[w;qAu*[i^ .ʾ_~Fbqޠ];OG:Elnvײ{w"{bݭ~Si'97zsͰ6~uOuW] ^Ǖʰf# ^fO{H $O(UcN>z7:W+֗hpBªT,رUH }Tnʨ 7נGQQw19'NTm2M6 ҋkIA\mv]q7AwKҠ]Gil6,gFgg˱68Qivۮ0)7U=~q!oo #PtOqTv޾_Z_7fiX?n8 RÞ9u2 ͆ Pe@@jDQO$qkSwpǜmQ}Y-HӋl3#OCH'[brMH/%=CBz^AmȖ[>y"z6T6)"bQсF:4VKF&lJn9HɍO(テ6H!|oRQ+"O]% ăT o LXٛ%zȵ4Y1\%j+a3z I̫'v)#@XJVE0zcKK*#\rqO0$h|KR$EJi40NecDȣ'5Ml14d'x>=c\gZ)#ਸ਼Ȃ73R{Kt$+[g. )wBR$wO|Rf8Z,),y3/IHոiʌRXI0xEy !*{U"n˜",pGT0-O@RUi&zrV=;7 &GZ^PH;YcvHTBQp|-ry35ٞ M2BW+?f; HIlGZzdAXSXTdӾc$4 eX~'xlQȏ('Rc0edɕ"5%,1Wͽiʔ]'>.-uS\YȼL911)KhN/I.8ZHѳEY9:,,_*&e&}o vptєMr[j%KZI5L O4p6AY#y)Sj>OݽH(3 sz7x6XD{7⻄RR׳>cbls 9wۆdN8q4+_LɻVmLg2(N  em5w2{=ZdQkahCugo<}Z/ҚDO9Elol:J ըe027X qgI% (E_bQ/#14}_|Nԙrs1ZK"ˌd./[!^VȢj=qfq&i M2Uf[@*s% qc0gqFeY-#}ݜyC$!#)ʻQ5|Ի=饴T[_Ʊ?zlWbhz8iI3ӖYnzv˲}]E+#m6FS:z ZC0}e{_6%jvA/¿igpr{ w_$JPH`$]w&߫tWu[ЃxxNz8<eiH.`aA{u.d ߳m1W*0+P5Rt#]`ᎩL=Q.CRd^A ]{5\~4U`隰B%תfl3滭SUFT}ߨ]RŲ-W:YK,G>j$Ē2$B޼8v"EdD3lxaDxHeL`'/%q'kq%tz/n u`d`tu=Z\ R382ֽd/R9 v4X&\0{=߱VؕݱVSu/Vu~طtǾ7RI=W_]޾N-oeˡd[٪*YZZ<ZY@Y:j c4IbGW &ƀ/2Yߴ{]qf}$ŕL>./2dF Su ܱͅ@dY"ao>}.qD:PaEĂm`Ur=W"Хk㿛׉7fXД>E?󯆟6I0wP,] ~ &!|1=`j,p3,Y)͞{J}=x2Võesh: Q}y{ +ql(Z)yx4ArS<ՙ6zbR"]c}5=wHiWOmfQexI3>Ącj7\޽t5PEC?b1 чsg'D3飭/OZ~_fԬ25r{Ƕ:fi^Yiwe ҕW8B33A読viƥ M|ަB}=YQ^ۙ`}M ڼ&;YQ-#X!sm08%nZ]Ќ&w g_Km>VεM$Gr3 & o6Cf&#)_#Fihfn 3D}-Hl RRhQAZzYػ@mm鶻nDqݔƎiamrgy],|ݽW{[gt`__z·Ƕulyڪj buxӘN^5ϊSO)1h?< }{HYg9ul0nݛB@oA|8˗ҎHYxIJ.ʞ ^u.nnΓ5EA7;Yӈ,٬<#øm&֯dX7``]vpʜbSpEa[}(s S0YM{+ XgtTs¿ElU=eNW2V>QmAۅG\[ʔrFffO$ NgwMײN{V)˶QFq`qwodLsM)MzI1^YfF[#Fq6ɬt-;1[mmnxfߖzV,v2?0"|ue#65^pAx04I@*Fۏv3;SOng @F7ן+ʭ״Ӗ\[a+%2S3?\2!ddujVqWl&+3#k3#tc7`"l?2i5{Jްժw@&1d`.f6g.r[<]yLL(J +3}&[f K`/k! ^k2 A YZmK qM*2+rr(O؛؛X6{ .7xf$7W0]u?l:m[xӒ ը- NҲL6‡mvl9 mg>ȃa^?OGB\+[2 \>7q~HJxnd8 vR ]E_g=ӈ֧Q4@+Lxؠ8xl'7~|Ǡ$Il.ӄ9%!D \GNPSNK(I F hV i>}WY\'R+΅ ߱W`6go/ͭ5G+\6D%oU][$:v5&O ؊*[A+M~<kfZ}4^hKzۈWƌ/Lv܉fdw?1O*^o]{z${5 XOR]=eHuRW?g6)j\'1֛OWTaL0M yl!u( umݯT~e>{ݣZ7lg㵍g3a[`pGKjefJl3'C?,|E5'=)AՇ*_,è#Ɵ3~\yhQ ׫N_w 1EDx'qO&/@`ngv1xuu+n%ۗu^o|N!*f }9 - \O/7XN9_J8>iL!DԄɊ^"22ȭAOHetq7q&(V2kW ;!QC!q}hdˮB N_jh ^`<^raC숸(gCT٥B(i H6IV4/Dx3Q 2ÒW:GӶ\t9▍8::/V8mw>k4>RRMBy<MN]Ty2R޽~f5MKъiVF̶%ml{#f~M8 w\.qr.Fv Gp%PTE0U|nO'"G Fh ?&s HႇE!/Q5f";19PdnDxV?~ʆ0CGWʴJ0#Os0b.| biQB9lnRdY; 6}Tl`j6}]͆b0qA{E&j7Nl%! cJNP/F1/<k,zuﲆix j"!PT24õ\!.qfJh# 4!>t8X4!} 2Q1`ZYeEšzU!D!_h>n +`(r~ x1nMܻ.T9*筨?l@%KNLrL-Ky?Zg^;Ⴃ"~_4{ضiȑF hKJYmv7 rBgRVemD;׉<ʛ,g/2c(?L~<\1iATr'sl;{gui޸? 8dҐN".3Ҽ!,hLO&q%@sMfC5Jz_a-* ,n}ۄ3yibɑH6%!-1 Js>Aw*SvL;4ic".O'?9(.=c|t`q}NhM9PE[Qݻ j過ŗwEtL0H#u:Rԟ@J}=U xKVdM9KǏ)TI2d#„! |Bߤ?=czE*iS]]|PުZtQ6[%' /*&Sqy{ııdMBh+ .?j#vUQu￈W%h!2uhTh ϲ7~@YptSVAZgqt pTM2dd5h phQ0@ulCbg{9&YBQMh+h09=:h/hkËIe1׏AC6<.Xӈl`XDu( %xDIx6"qzy'Rm2"*9Za̺{a2beMگ+iFښ *VB.$)S0oyٲ-߲ݦ]膞X}ǴRb İ(! AxqpK{:82>\&^Q<  ˿'#}F1͡qI FW6D>eѫ'e҉$(\?<0?#D~ywau}舟87%I4BCNײ\\ފl rcwJ22_qxmxi(.^|~~Bv*XnvR0BQ`!Àpɪ#<Ѱ"#pʃwppUc^EÔ㑅_>WVD>p= 6W[~:ha --h~㳋g?_Qw~¿Qϧ_~9zq.+Rے$G|οsȤE$LdvFEi@ʥ ׼Q*m I5^jJ٬mY5lc:}q<U֏ x[p胛xË?$ bem թv>f< k&4[Ŧ%Fϩm; a.`R < "iT~&D</kAx&|U4wnZRw89> T/24%PoG:ED-R!qF18wIM O̡$ %l"3n3r2g4aagǟ tpI`(@K34:y5Q:(u?7cE ӈGh0V|/܀, _NPKekAl]bW( eQ]1l>SRUtGa#:+Y Fq o8-ڟ6Bo wn}ٷ z7C?0Ka˷KxST|+StbB1T٫)Mopً *3&xWYZHK8ix7ǩ@G@1 !!IZ9ElTeBcR0#7ll 3#}ST0[AM3qqɄYDp:fZ9)<rc9pfeTD5l7gV1KkPa)iTehZimƳ[HeUyaxJAP~Wn׿LTS<¬YlY?e*.wpVN}#p3f {s : m!̻j2[M6<] >> 26P\ف۷.k;NQ6߉ 1,z2aYg8`\ᮠ]=5-ޛvb[| "|uFYT"-\:YJ֕k3f4OiVȆ@i':eŵhKVi"B;#z\ioqĺ$> x{g,-3tZ1擅}ahXr- =/a>5N\w M!֫ǎ ebYOXiY'aI Cqd@\]@IG0Y!wVG+)TV?V?-W`˵*;.ݶ?|҂ >z/#֛[?ǎH#(4 &͉.{z82V׷NXj÷qq6%Nx[(J.$d^PQ0ZHY2h%`D f)pRoXG)D"%UjF6l\5\tyabD`"+RVe8/F9n)\pfnt%_};&8#2E8iU>}1bQȽIBR>{>=QFqa"4dxmQ'u@kiNin [%e7ܷ?,sWr؎/s#oxGGI)ƲErowE^{~Ig%C?;ues}?nשcd{+V0Ɲ|-HZM0- kZoY]@Z޹q醁;p<ĔN%BDp$c[=;:V횦NQt۬jh=]Z۪Z[!zw#={s'OD"F/jrUyhpyKrvI@%EO'ӂh&ޝ#N8ZP&wV=% z I],%MG~k@mE"M~Mq7ī VRbHߦhB0?Iʠ&*?zTb`1C( QϢ- tW͕ͦU-Gs~`ep;K $?&6uq& B@4нeqLPA,:Kje6lS$ FD3+{I+)%b$ԥ pYN!#KCI{0Xi0إ5/v61"Bty2G()a7x2J!M#X \T%1, H| hŽD ˧PXRUx ItC;i rj\k$nz @;=PC8jX' ,FNIp邘GQN `2e<>?6\g$Xsy dcӲE,=_$憹5fڜJx5]u@1HC2$H%BM$YD{xg5XV͉sc:}iY׆Y*6k sx%pl)^vE`b|Y ^*VD[ _§_ruxLPK ɻ`PKaxD settings.xmlZW8߿gtNQ@hPBh%MzԂt\=hܛ7 "dPfN0Ua8O~^fk>Sy*Dv*jWC"5",jҫӷmk% H׊4MgKj}[1: _e^c응ڰ>LƬlTS.\MPHdX,k/!NߵVص{F X\x{#W1! RRQLx&w6;}$|^.H`˗|݀ϰ :bÔ1-\K|MO>H">]p/h!2 ABL|c-F](.S㬻9x\8I^zH_} uR"o/g֕0wL>*~No,A=\^yr^;7_qkh:O]-b[ƪj[KǦ1eiUCiOh}UumS~ؗN1/"dJos?=3Ȏ}3~cxztHo< ]bEx9WY:jJ):f;ĻuXo2<<UxNq?o<~D'#H4/p>ꌍaӦQ7?%[fߨv {h8acijw`857Fz1iEBIr%ߊڔ:`D"CV7]VV7\LJ5ttH1iU`~Yg1Y*7 ӣ)i& cI shgj>lq p~Cf^B=蚀Q4##4U3q ڀ.3-}0 g9bq;Hu? #fJgo;i<-9.4_aA4]mnI0ykwWmPKTA'O)PKaxDmeta.xmlˎ0} D%bFT4Vj*u72IȘ!}kiF?sMp9 Zj (Tǝc)H]Ɂ ͛3(2YUMoղS],tI>Ѡ\J~EmU9"nQPgjLS#(P#"hb -c%iD>ݧ 1=G#DyFU, ^$}ol1ϧveY_ 7#g<8uD'4N2t u>L:>!^^B}Ï>g&}(^!j.?9\ E4Ōc,k, DqQ8X5[&8Җ W8jX9AH7AhLzl1PM{nYQBu(iV 4̢aGêӼH1j#kG3q f n0(IZ+]li裱٦v@~w1vuޝXt7%g@~C_PKJNPKaxD aa-Pictures/100000000000037400000121B346EC85.pngPNG  IHDRt!.uaIDATx \LV*RQTtqmYEEzwmW"hZ/*-RD(K[7wij4-59yy:3gBRSgyv|f>F=/۴" ˯OrsR?ybhP}ER.lXfAæ*tWIυ8FgUY-BvL2eĈGnذaUBH`e"N]=Ew$%%YP^=VZ(*5qS2\WAK6f.Plс,"7/j=z={s 4cZmGtdh(| p 8s挗ȑ#ͫ&e|q ?T꭬)lңw;ע_'%hh_t󖭴ڴmiӭWWMv U[h[BSV様w>KMJ ?vjj'U_ \!, ETRfffY2Nq¯B)[ߺ^Ezvʂe%K^ ?iu+fND[_N^((* LT;O:^*kEN6@%뙒QXPp), O[,%%ݚ#ѻ_ou:'oߨ\ \D)9ݼ훞}}̥^Zijm=sמ6ްwE<Ҧ[jۜoe/| bp u8_))%5v”*Cv?" n\֠:22c+a<*IcgS=vo^>'v}gTrrr3jCBHw#KN1x !dҜ7Χ$stXᴋSߧעΆ܈m PQkѹGϑ7D5%0n!IOg!a!*^J}ac64Uz-A \k.5K` %Xp A \k.5K` %Xp A \k.5 NJ ݄%.'q؄p bvZq7Xp A \k.5K` %Xp A \k.5K` %Xp A \k.5K` %Xp A \ج_^M^mFq*w˺inB@F*`VqBr~HwMxrw+5K` %Xp A \k.52BTVO a):EG.q *VW[5=c[E`wg|-Ajj~;5hԼVR[UbAp 5W 4`ߣFg))7wFj.Zrs޼Nyr|xِ@7癳ҫؘ'n .fWfz:p䙎/xhf?6iy,&/ts.MMQmQOrlpͅϴ~,Qz!"'۩kYyC'!@Cǃ j6:tי}|Iwn "URLynv67,ލ4<]i<|<舐6SaVE6w$}6xOVk k99 _!C'Q%TzH?Wk h.))i6i0*jq1O<\V,ɦR)0sZoHZHu6ОsgN cIO{原2ĢZE u߽}|xnיht(n[󦸘|,Jp-Ls8>cA#2eAw<=t<5.i/Qt0EF1-gJr90b$߿Ӯ}CuM퉳>g~ >wノyCisl-aQhI =`=cS*IN -ņ_qq I))rʹr:'!\CǃZj*jt}zϾx\t0 Bz&̲sYTK, G>>S1gA#.s|]ٶ]qokaXǿICurDRQlђ\']/yRZsVtx̬}?KV37Χf~iE"Q,${xP!\@MŌkZg>}ƊMxU[ X3<%Ytc?܇2?~nRr|- X T'!tCǃjW[66jL;ׯ4.6M?ge6m]%+nVYU--%D݆L;x/׃2mka+%`'!tCǃj_ ;dx34mJ;oxv1O\Eګ$}beZ)^'޺r)‚څ[LSF&W·8DYeэbԴJjrb뒭WœG64ψS~E5 dk#W/1:1;7|/44[={rܦ*oبF #[3QӮv~ͬr))7|- :x=t<u %&R?QA] (bm ؘ'VI[{3JX/))9|T;zꗵ^ o]3MLR^sn^>|p wtp98p {z=.Xyhb3eˑa41%*j-\ts}[hb6wu<{Ց&mkvUISݾ-j.ym&\Tyå\V9~gizkTt<̪C~e1P_C7_Mmp|BtdܹIPN9!H T{%gz,ݪ }IFYEvmhSx [i9]Bm~[KM?Q}${x vjd/Wq˦Zٵ 3,IMNp###cyG5yk&d߿unz~{A>v1)&ps14EG,^Q[Ki@99 ("Y٘R$}6x"ﴮyHs-/.x^}x~x{6h'J-.u5+pu6DծS9`‚u+rst,62E}&˗ݛWZ[NRSHILؽiuGpi>eM Bw㪺E+tn>ΜgGYL,AQAi&yyi)QAKbx^`?/*?{郻z={4]|r)g8w:M-]e?ݚpEYy|d9m%LFKzXXPWHȁ(.WIr np'IOZ?^ث C"K7:Ϸ2ϋ5ZyumD( cmjm/Q45?/@x1w|dPtsU:9B>{KnיtkA.hsDq*US%7III5[G5֣wۻ^F twoKVU};t|".sS)|x9STm1yNz=+6ݳϜMfI,*USc%jm=sמ6γ_Ϧ*.L5c8Lnʨi薒С[u>nWmskש TSLI|'\li&K2+^x)M<ꭴv)nij[(v۷֭u 333UUժo@\\\޽GEm9r$RfajjWb:::Up;;;Z/,c>-e(5֠S'Ekrزe >eBuǏ?͟?b٪R8QaÆ$e|RRR*{:VA lϟzL}l3v”,wY=aoz!"'۩k^?B } l׫WdIi5u:vaf^[تUw^5)3##ãr̜lc_'%]n.yV>+yZm;mqϞHIJnk5c~,}pwyu3wqH #+"SES` g):lCa*ޔw;O9rZnn{wl@i}$J?}3%fVPS~{qup5Z)lr8>cA#2c$g~6vDM%=,,(zty~5ĢLoWݽe2qiߣO{ܤ :M-]e?UBktgúRxumDp3??n^:E*UJѽ/5wat 6[rk׮uuu8KXhCڵGӨQѣGDz#Fɭ_^6Hֳo}Y233ӴiSee![;6sgOF29zlݮz={ݹG/aӊarΟPkQm8g%kjs?PT*ur1QJ[~] >t"}IϺ'N,w6===ocN~ NEł۸C;UՄPC?32W3.(gJtY/Y܈:O3)UH;3>ae%Eem)?fJUeI@3>G)\>{t#(LĂӇ%.)ko,,;>~x/p):ܙqz 777˝)EQ?W>d} Y\DW%Qonܵ\u e L <6T,Eɩv4&'޽qųٽq6 i?~xւ{A&t6iR*}-JodeR)*eUwcW5Tɓo߾nJ&Srۻe#Eڻ%`*U=͙j vX m73x欉:l,˚xTm3eI4[N >mLTSנp&O|ݪRR?~ܰ2^p4SmNė|j-iС^d$''߾}.8S2>{׳%S̓+9*H<nWmskש 53W!T2\Ve={-_^%eJ++ϔ{CFFvksw7ֺxRYu{bDq!Sr`k5`1ˬO:]>4uxuYJޥ'v) 1z={?BH QnF_ܺrQ{PߺrA:Zl>ɓٖO޳gOKdd[QXP0Obu+2cNzZDU2޽(qρݒqYtjө)iG G.CpO%S9c>W^>׶ל>t!fmeJw$66V]SN bl@I>uKKw5Vok.38|tn&*{ R KW7jhjI ;F _h S] j2lxFip)//W{hjk#W/1:1;7|/"x!p.;|3nii9a„J,Ȋn.ɉ2Px I}5+:jUPn7*7(fgO˶n3aŦ.>\V}d;AF@#'r$P*lf@\vZ.> '˗ݛWZ[NRSHILؽiuGm>eM%G x{Da'Xrȱ)E[N/7z)k\F[hȮwMq1=\vdfKIzye ٠ܽ׎E{c~&`ꦨ!t˼Eb3gE[P,,(p]"7'{=}*^Q(xN Zv8keܜض"n?5qH;`-\2gSz3tUӭ 'c?{ij3{R_%7\9Ut_q1iڨ0%L)(:Xj^=//-%9*"8a roWݽE}9M^5m;$ff39H)#+kP|eH*̅q!i(m${Qъ^%9r$Ylw*JE+9{^ͫJa3{!b_t|+c;kXQ=RNm6XJYE)Y/ oQV);qiåfke ~YWW%_2/u>=-5cF~~`*Wi^Zl^AnMǮyUZ,XwKw(UYN)Wϙ||Ջg;v[W=z<8ߌC퀴9-B*z ֙tR/c,ѹXl^}3V̜ޝr%%WWɟ>d}WU3ٝt~=xQ ݧ)&*.22`Q߉38H;|TkWj|F-tsZK;hDVV>+ف{6&M3RsԊ6֣nVƑA} ݯ/(Wih9k]W O=TEp~^} v+gt4~kjdl9)4 g|"\ώ%+Vprr 8_a(_9޺^-lN;t14ʰ5~taW\h^- --hbN542\G\RŔPw3QTNAfޝկ}NڟtWh&Ыh?_jg?}˫ėro:j^OǓ(u9!\mWG$yDq{i.k^ )QQkr􄧛3܇vlV6[8Ng~8ks_ͫt:vHrvo^a JMfۯ6wzTN~/vzw1ko_pρrrUy(Z~~ӊ>dH_"\Bpވ]>T:^QӎUwoZDZZ3'W4bL}&'QfgԚOeG'ƿPS>Qƻ7ꭴ8dߘ/yƤ9 oDс: g.[}:yKiH ` w't9r"K߾(Ti޹GOI3Zd ^oC;]6W=wl jOBWSpYBY<o+9 WpMi)To OnijAW+Qyx$$ٌlifNML]r 5JA?}sv< rq_KLc8LJNLj{ rU_/ϝ9c?`,n.܋oq`4)IKI9+SZ}yU{ݮk,mtھꅈo=f8nBcQfI=j"K޼~u>80WzL,Dp{aps14EG,^QL!SnNΩcBw="=\}w;~57:qP =7/4VT>w$%W(QOESE2?ut;~LԚ|.\kw7`V*#Sv+ˌNR?L$X)`?LZeC$.6 w}HxL` S5ggd܂y|d9m%L zw={:z<*(P$SiDXَxbdOn[^WEr`:7K= &β5NN9#J}ûtuMmR(?lM>}1Tsx.PjC8} Ew|wXA5yxPK![U%W#C}=i]6ӆʪji)'6l ̐4ɾr#o^(r S7f 2x.Woq4ݿ}wEsZw64q_8-j-iС^+!4;Ozd2q^,D %BzuRBץ ߃n^LSo%d9ٜ#R~zʭnD>oH۹TxŐrrDZO.8$.j=wź  Үv~ͬr))73854HKLm^}5ZKY˿m9uPh7 D jz=$N+Ʉg,%%%&f2^YE-.扇ӊ94=U22Eܜ}N i>uVoΆs̉!c:Sd=/wF&Ԛ|.\kw7ͅe{ݮ3PܶwMq1=;RY4<*Z6d ;q|╃FeT=KuK|Qt;t̸i 홒.\̵9G)o=-5cF~~ GO+_ *p )עedeܹ[rѮ459W7|S^DYvu[yf(,,t۴ܙ܅i4tX3 k1bTlSY>mc&<-jsF,AG㩙?O,z FcJEP1PL:c殺ֿ331yyަ]pxtdh_~g~AR?ybhP}ER.lXfAæ*tWIυ8FgUY-W[zJ}aƔTlҔlLKV&*(*ѭZZJgLɒ>M^ʻң{v( Q֯ %T̅&*V3Ez3WO9JM6A :2&y:`硉45K/GĔp9z͙[hbN8n4I,Z KԵg}<ʔS2%N%XoӮy`ѷ{c'a-B?`jM-q]T !$)]ng*/+\z̉LׁOlf/,9:&1pȠ/&{Mi '7t,F"| "%G$cgf<USogA=zg*mѦ4s6ʭ/ \@R߻׫J\~PriRR[jnM^}_5EQ6|3Wma;o MYb}.5))>6؝Q.GW!| P <~׫/7 rz,T%BȩzpRڹ_yխ9m}9y/d0`0MS_WXXLZ=z4ظ!*8v9zhq7Dȑ#]tٓP.c*!~!߻]M4er|skhDQ#1OyR j۾ԥZ4A3k֬:RebVBʬ!eaenNmtuBu~ץssWo(x=7KѲ/"cm695.0s^ի/xP2Cʬ#2ų߾k66=DMs6|I.=//-%9*"8azh2q* M-?edru&?v_QL,W)P2Cʬ#2!lrJRJFLeRG$&:PbY KIf5562(/?~@G kn3ˮQyW2eEQ2eE %S[UJp^U8MUzwZ kn޲Vv-m*t ,l fBv2?ܿ]jRR|l;W\uQkcΝ֭})XaaV[!f>|`(*<&٣oT4tR? z jjjfff.̕+W*BکW)233̠5~xꢽzbF+d 9r_k?׸q/ s9ؤ)S1w᫄xnD]M8])sLnNΩcB ﴎ1ڻp(=jK.6nJVӶJ*kh]BRtq6YmK[1Bb=*ݔ[g3chL>x13_?duqXF$ajcd$9{,ZsrrXl0j(CCC^x4utt8>;˖s4d?cѹY8F!m/iZj{tޮkVݹy)JL;G|I4]gJBJ ⢨` wm^+B|!RnXJzjBLB[#-#ףDPܩ; %-%=vtV_'""A܍ L9YLRzL5u!9:w3玔7:fJh킈ԜKI.],4KJ6[oJTܫr*YD+Sa'2>H%6EqQ윌Իt#eU/TRcv-I*^e1ahi$v*+2 :md)BԑN"O`BS_,^O f8𙒑W ur\dоFedI,/y.K ~C1?+dgǐpYRE#gUT{eûf|+Oh^˩nK(af=m]nO-ߧpYWSMNR{39.b9JTNGJHT+ +1byobR|WY׳2|v8Mv*ϘuUZdb"/գ*#ة/U k6i 2轻~ʴoeǽ(UBrej+xZIH%F\-]DT$\y9y+TH(soXU'YdF2K )-SsonjjTI@9i\eO(z(ֵ޿ ~Hp \v0=|:t܅]f6U\yYZB^o'an#^,mt dͼqЧZ q(c ---,.-ޟN>auć:{twJK&KݕT: !\btW%٩ O%ƄݸY\HEjlckrʹ L') ,z^^6Fv$Es bv(䩖6EDĄڊ!PdB$+J}|/\f?4} <Pgbb2 E1&zZ_iKvi%)ei+YXo6Aqv:$,L WYI,>y|:dg0WOK3f6p%֗f?N=m.K_=/Ҋniw!\b}iw!\ilg\~B~A2%I)1:FJH4;._(*NE./N=m.K;==)_)L{S_zEL5;-1HИS_lnAT~߽ϋEg8=i#p >xoRSJmp6J %|^US'$ZQe{oO>.sYhnx3'&vw.wGOەFVjj )@@ \{c#+J9B]V>^Ƨ*YtXpX[j)+) :T>9J*7uf3'~}8\/S%axm, KVJeT "B"ŧ%D^JI 205Xe+-+AK@>uZVFKs#lXan=RB`xP_i,w9v- c N9:AO Ν:c5ͩ-?]%)6"%!&1&<).rCK;C 8>~}1ɪvZ+EDEytDZDs6]rPy/L*q䶶Jʞ@fO_-,[||FlLBN3^^_(S>]ԾHw \%C-H^aa2'#yѽ}n}Sy1rAA%n\oXZܮ_U y4@kY #~Q5UH&}岉|Mm|4T,dÆ`!U2)!"ѽ<3!@Cv0̼m+'odeDߘz$FK6ZHjlpdu¤)jsm}`a+LrU1odz>thYF|9G#gUT9*9.u5$}Q_dhi:{ g.@͘-YJzyk@|xR}cC+{q{ycC=PܸJ ~4ZlB1z6YtL$ZSS(IVwL~p Kv wJ;y>>:{C;$$˔cw#%$ -"s2Yٓ#=ywZt߿{ukM[.756g:?#r@bO= rHb3]I~W2FMWsoyEP'wYidiݦui$_b 0c^/7Oo:qI=қDqS)1!?V(*&&#P[_ȬH HpZ>SL?v:$S# Bc:n&!5@P!\ \ \ \ \ \ \ \'3`&4燖AIENDB`PKaxD1u1u-Pictures/1000000000000265000002240C232FD2.pngPNG  IHDRe$˹sRGBbKGD pHYs  tIME E. IDATxwXe-AoF-V`A vEDVvco1v DQHņ"Eeї'sqq͞ӞS󜙝Q ^Kz @/B]ûwU+-@Ձ^V_'NCl\ݣclΤCgbc<{ngTW/].`NQ1r |QBMXla#˗g>/-=CZMgΩ-5y,*ħI,o,X8nzͥeeYH1 KnU4EM=j,Rظx&xCl-x*]Cm!X;wﵶ8ezl\XBƒ% c[0S!E,nӤmܷo1A.erJB.]puvZqSfVvnܴe<T`fVBHIIi?:)*n./:;ݒ%ltWg'E+'5/QOHμ~9zN{yhv!.m~b0K=Fz}O>+>)TVeL œ':ʒK5"y7l6k+]lVϡ` dOҕkM6xol7ɞœn/Y0c:"zqZz*G^ 88߭;`]g:26x榽MxϿDNۼacnjJȤuuc! z:t יNF=\NKr>RDdٹz,mҤD)i+(G6}1ækw&{ƶ`Lʜ9cj?>5K<bD3SE8S:~O4׋%DEa@Ɖ"&6NL,ܽfrUU޿F|ՐݺB]:R^^~QwY_S΢fdf΢ń f޺_T,#пahij.gȨK !?ǒe x]\h߼uh.{YnwQ˔RWJc^xxގ]{?IUX*vJK;wOqP\kD94"K+j3E?VWW߳c+KF]:8iǮ=.s~a(mȘfq 3Q;0=}ŒE= {m}7-{ƶ`L-Z4Ǥ -Z(.ؼy"Qa33+Ç!?c~AW9orstoެY߲E FNIMYf?&f$%eK4iB;9yeKՈ&99E[ӜWҋݺ$Ky,ӨaC@˗F .{]ۼi'>x&)$b%^,00nFddDth!;@_/*&vCE$S^Y!7o=xHrù@㩫OrL{yor쿗!u}ee7S#Yg޿]g5mipC/ G 2lnh/Ko.\`X?żGa=yO(jiit!yԕxԔQY),|mǺ+/^w%AW;.AԲE]6Js`6mZSw۴iw<sd\1H&%KZ{ɞ&'["Y#Qb/ZJk!U,isˆwv;[k{57z[4cڵkKyB']A6Qۂ1t~ۡ}{㫜A[/&>Mڸy+}%z뗓':>z,*:/Q1G(c_̦/buخ$GmfQGDsQWSvٲ:}igR\B}c䛸#lRҩ)TUinQ.r,}͒+!שk'Ϙ񤴬QxfLJlIɩLYH˅Fb7RRݻuMJNOEw)$9o4P[7c}X\~S <43%qtӤ侖Dal th\ܽ@#͚b=<3+{]`P(?0>/*:fρCϞ=us16)mI8U6z*69%mނ]۷MIT^|ebl4izo\nݾpiw)< gr/'On^4iߟx³evs3Z&)9eܷyڶ^߭;޿onn1onV-scRݪn7RYX^X#E~g~lG~ZP[44KJJ|l޽[5Sta=c=O(5bLG[:g~AAk-ٮ΃gyٳ-[0旱KPװ<bchaϏ* "_\K?p.%%s 6%7|捡9Geba8!<"1ByyuVc  }(vӗ Y/r%f嬍\W/#DFS'O͜>-^s!Ľ ^PPHYayF쀝ܼm[ 4mC/##{$%X xv6IɌ!.M-;K|r[;XXNBRPdžfkgbay;4MS6;p9Ɲb^iۼ׆>/p!AR2I/ڃ6nhؐÆ &DFEoۼ͘:eNӦxA~:6x0{~}UUUFz~A Cnjj=u*Ҭ}`?/޽++/wT !=?߭DEsQKYل-M">45C R˪M?}Dxʖ0t:ya*<6"$*D[oOe݇ul;В%!t2ҼFžeC/^ְ^ZۏzLIKi؃{+!!:C016:~TiiБcBz<~Tyy{BoK<FEEB*, (eeb=()iJmo/^B nBEȄn=rʭ2c=mn%Vrr5V6ټe/Bxǎ[ ĩ3S&Md ce#8h׮!\>xj2BuvԘAH+i/[  wڜ&z@}{+eHD%!ļzfK:M m$.٠fT\IJg|PR .s\̿LHΆȂv'Ka?6!9[lW/0Sc%^Kz %^Kz @/Pzy}CSs8*&~@|}'O͜>-^s!ǎ[ FLJNlmCW99ݾ3}d:PCCҪe@w6x< ef>PzyfϛOC, !|>_KS1$S26" !z׮(--ݝ1\$vRGSgL@~<ˀ%`Fk#Qʹ$67yW!ńv֝ Ӓ ;Ш:,!9;X˾=z)*#!9[lW/0SԞ`1KXE% K /KZH&^Ex r yP^KK*eGɇ*Υ\O^ypz @ͮ$ yB/\0Ya^Kװ̜0u "DBbbGXX:8N` JnC&r٪Փ': ?}ʕ'O͜>-^s!m jW/ORQQ>"$%X xv6IɌ!MӧOl?~^RPXAiղe~Acb˿$hhhs k޷FV\uPUbB45͚|-MMƐ kh`&_ڌO xbܳ'!D_OWBBt3Ȳ X˾Z/+fuxah`q7!d6oަu k0Tim\㊶"?z mnve*@o˕j*Ӧ]DϬM@Q¡.e hr{-Jd,? p U˄l,+`-/Y{ٓq}.ߢ%7cV md)(k u` N;UC ժI*D1vS9IYҢT$-V {/':fTLQ;PUyUnJlOC_a m@Ēθ 6!V-s=SaB@,7\Bzˏp*UF;c(կJe?VN,y buu\B/Ώu0`_nS*c%jg BV,e_r}>Tٶ@&tP;FrkCVGcjF*RV@}9ʝZ}EɚH@= 3*[UYr:\{f bYszPT8YWT07CUyZN'XWֶqb!Rt*ޞ*J4@i'^hEK̳02*,Ueh<UXPud .GǦ7'E5cz Fʊ.&Q/bb'3zc|B"bm?qBxDc ֌5e+Vu{Yᵆr䩙ӧ ={.1^C*Kz{3U*/\J.4j!;[-wBS|֭xv6g3(DFeLi`>R/PYFL2~&ܝaCB 544!Z/(` Q򩿲};XK+%|Uلl}NXH4#\x7U)/]Vs8y˴Py.Xq6"!kij2w#\%I*%'l.}O9ԏ^> [lbڷkG^~JHvwPIvaACt?-jR/W{8t9!dc-|ԙ)&2YQ]_s,R^[ >\ mnveP\sA3ꟊR ))~,5))EQ:Du0ۭrbPblB/f\HhO|j&$z ?\LGd(K@z2pYgBi%`@/_/9*~ p.ȅdT KK?ۛ%_"'6WMX SZ9u1KLMAn~%{.bgJ8vSQu L]^/*X0xeMQawtxzfZBN{뾐J+6!-,k2Às[K%$QM`Wv~jv ɑ.p'vW%$gSOPclPY^$% Y,ԩdֽhJYS5bɈ1PTD+*w#@q J'WU*nJ mWvyT_9@ZZ;e0.dWe/c[z kNQM-K]reOreXPw¹ʏsƲ%@NE!o=$^wJ 8A ?m^ +kcTL( K !(*~CSsɏW~GZpчW/;!O9}ZؽP;[۳C.z|jŋv{Fǎʯ^;yjۦoʗbc5$%'3E?vꤡ1͵Iͦ_/%RPXAiղe~AcP}233 ƍ9+%Pۨ0 ˷vRKS'|-MM&M76k|To3c|B"R_OWBBt3,|G{16m=rrJKK\GSgLsitLLC[t1i]:w;fN} ~`4mE n ANɁ` jDwEX8qV, Y03eQMr,`v)`r+eQJ R aGoܻ'QQ'+l%8wnVtj/G)O,US.Q)v`ŞJq¯TzDb0B`YYٟ'N_X(:<5-](%yd(,n诬F޻WVVp!nXŶ)8kメۡwmGQ.!X]RS.X18(+P?~ lB1B0=# ydGq3?Ԫ IDAT/5-] ’3~JbFSKJj9Jb  Y5%,SS0VdÇ8JW_F5O]jIS>~x^Bz|Z/MtL찡CxлgݰNW[[IL!$$vv` Ɋ':GF5Qصܱk>%o">/҅l/_4kkWZ -Ŏ3Vyw=R_9n|>-<}uS/c#gnkg+L>6h0v̨L:\WG;6>M_rdlԓD..EDF-X ˜_mR~}Vz.K/NO2RT_tKB1zܯ A2&MLtFYҋ#,=M]Mm!Y ~Ȅl5yܸĪ:%5zPgc5^y0FGW]Mid-cRX<1'1%NFfjTSJu+׮Sk>Nʒ;mؠeާ!fԴtnaT3~3gLg٧QFaǂTL2>!Ԅhi;:.N?| c !Qѱ=z1q;wY\UﶷH,|ئMvB:/kymGF]8wz)/_._ ޭ+!yߡ#uw%zZyyy?u,x|`Ff,z]L;okS'Nشu{LߴiBfVorsYB!-nmTĒ?V<v&Xrah?O1uht]DBӤdlY3\ܷܝݶ`dL3ȨpMZӆMIM{>n-ө2 }۵\ITuE͚55[}PD ƭC+!Q11M.-Z(E22*6hf߯EF!a#~3[]MmȠoh 42hSg$+ހ32zRBhdY/c zfkן<&Z7oڻK<= {DDF}]ss,eKJK+z%1@_/<<@_oǮ="v2&Ua؛IZ%%%߹{⏃2^#ʡ1YQ -[Y2yn.N;vq Co @4?ڹgV+,i#%5mﻩo٣0c:UoѢ9fyPGz٢E͛7-6BL>3?|q<:|&7K͚-[`Tuk#ihF_F&ML57BDm@Q ?O^Na3W.["D4)?|(j\zϕ^44{1lȠW,˻p҂bguws=>?P)ESWz_1_KS:2yC1֬yҲ |QT߽{ߪUK~qfV[|Xz忣%=݀s\؋rْ+G j٢Ůmo}sWU0u6imڴf OyŹ 2.A$%Ȟ&'{"Y#Qb/ZJk!U,isbݺ^ji`i kזzOJ _أ0c:4?u&7CW99 JCV^M|qVJ*뗓':>z,*:/Q1G(c_̦9u&$GmfQGDsQWSvٲ:?Uz-^+<%2QX/_̔?P^^~F~ Oo o⎰IMKPSUn2zレsWԺ:׮,))r*-pYOJhFƤdM3)9U) iHFJ*{I) 1趇$&j bLoko fĢ8NN]}Haи8M߹{W9 Gt58F>z,,qyyyfV֝;j˿e=8~ߊ%ԥDmܔ?|(E~YyD. w?+c},;iXa޷pJUޡRWWZϲ9]׬۸q֞=Vzڦuk{_L/]Ę{23Бٳfx[gSe~?ڂӜ:iϖVvwk mKݜJŲDa1c3lW$bhwcǞym٪x1#l1*" EQ1F=cʃX_r_~&*rQ @x? TQQKzY R^*9p1@/n_/ˌ SXX0)I$!$*&~zo % ?Ya9p&P,$$;N_.zPd9A6r^.[zDǰ{/]r䩙ӧ ={.?b!aᏝ :+ ;rވסmkуփݺM#SVV***ߧZZ<&)9tue?{qOB^y3rLoB McCS 3J|m|EC$ chjt)A~O;4 ER]y/(E8HPs xNQG14m^kC^~_Љk)ϤEKNA<b@ q?L4G:;%KBȧOtŪIB BH- ^qӖn]n׮#,^ɺ>2RQQx~φVFBО]b/f,L y;uMm< 'whۆ> 4<~q !M43!];utqe}',R`beNyRCC;oXf,ƍ 2YBHdT)y<ތS"Et6aÆsu _^wu5;vPZOn扆?~"4kA7 /+/w!=_ݢyӉًJzIл !ZE|>!kijJ)UUUEUUyy<^eKX:UU< 0nJ!BBT/^}Ѹ^ζ-YBA.C-͛j4q/iZ{1eͨѱ'={Bt]QZZz%$DGdc'O 9v%XR 'L#Լ}0^䳍Eh Ew@~CX/b=nz)UoTΑð.Ջ6#@XrW<E{.^ߠBxX襒*`%Qr[/mݾE d {!{ڗASֆ^P$?YS8 XҬUS/{q[/eo\s \qSWm ȃ,[~=wP;|TQM#{Ъ%:3]e_C_4▇Rf\8z e5P]MR쬴;JC&cD,.W+^b b0*eMI8_ř&}*4.KUڌpE՗5^UKHX..̟՟X'8l d:\5+G^ߏU-Ezؖ4cP !}P,)/BUjT*|I=xɒ<ʖޛO˷yyQ11L,,'GDP2M~8bY䡓pbd Pzͽ9~ڞ=]#}h_vQ#R%m;W?}>@Xֵ^;yj&_ѐ<&)9X,ƹ}BC`lԮ^سO]DC 544!Z/( 8ljȕFa4f3uq>/Yϸ8֙1$z\K-M">45JB_g7UK-spQ׿'ӽvh{+!!:ݕd լH7ܺgwנ)dڳ6Or6oަu k(R)UNV9GUE^嵦9Uh.]Fǽn^ƕ^'_5SXexW8yh/y^k+* cCL٨؄l,i_I%`-/䂚W4=xq7-V3YFUdX_N֌'T'+dt*m7cMƤNɒvRXv$_3PIYME5[OP+u|Rd+Q}l`rr^r νWV0'3ơ+XemI;obzSmXR VcRG{2ŬJlު[IK`FI *&~i`\򣡩_ykQ/\nfNv/S/{BDlDxlDA𕫁/۵;v\mLJNlm1˕IgVpة7^&&˂B BH- AZ}z;0m233 ƍ9m\[TTTUUA[zYB|&fzyz]%^ 74iB߼SPXج,)9mӽvFii镐*P>`֫WPC; IDATmfx~x^"rɜ~(UtL찡CxлgݰNW[[IL!$$vv` Ɋ':GF5Qصܱk>%o">/҅l/_4kkWZ - [lbڷkWAԣ#c()$;KKTv l ɊPV_̳DU>‡ڌ02fkI8eiԍ?lɓ%C8M.lz2bPT>A>}M$US.!K,Gfj™؏^z @/%u^Kz @/%^Kz @/%̨~xj: DK4cRŞ5`"KbEsw嫜*wzbIS ܽ~qbQ %{ƶ`LɁe<&>MZhpv}S3^&n:+Wgu7efeWM[f9MznAffe_,))!󧓢rrrz-Y^ NMOwuv /X"/?_2Jkԟر3_޼}={jf_y;[K6p1%p}#G^x~@'OLIUX*{JK œ':ʒK5"y7l6k+]lVNϡ` dOҕkM6xol7ɞ\l,Gal t*DxLKXaKguGP΀czyL^F<ܴ״Wc}z7l`Qtvl|!$6>AOG:ب'i\]Z@2;9ZKWzQ.lxē^ޢ_ :$+[|!r!6F8ϞX5YlK63 F`LRۓ%M~q~s2sa_"ddf諪abm׹\N-8҆K׷a}z>aSҵuK= c[0SeϜ1eF}]% RRS1;e|B Ңwt\?EEBc{0cbDski'&w[j*Cmok#{]:R^^~QwY__S΢fdf΢ń f޺_T,#пahijV?ǒeh]x]7ܯ߼uh.DzYʞMJJK+z%1@_/<<@_oǮ=?hƤ*,=YRR'8(c.5CEЙۢϟ?ٱ%.c9J0`dLcqqqȨ{mY_bɢ=RRӶ= c[0Se-c:-Zl޼EnMCb򙙕Ç~E*嫜7]:H7o֬o٢H[OtuD3‰FNriuINN).CQ(gk7x⩫ms?ɒM5l(TUU?ҨaCsWy}7md4dR$[B?mԨu( !d}|7E=phd+#3+zIu8x[WϞHH|}OTt̋/+<Դt~ 5Uբ">!d[jjjs\5h_~JݺN1+z,,qyyyfV֝; PH`|^Tt̞={blSJ7qT mrJۼo顯'“xH_W'iҦޒܺ}'..ay,oaQ'Q7pa^ODݼz)i?BMMkg?>fIRrz͹oڵmj9Q1[wcVZVXMiIUX*v{JKsKgEccX:!$3-3~˲~jCm@,))ٲQXxn]׬ZNmr҅KHܺoLVz.<٣0ֈ1Qnݾsȟf:П}> ?gϞlrØ_B/A<^^K>[U眺]o{oOXtNNTI( BkUBw]FycD"9yԤ=<2w|iƎBBS47/HX٩y<,p=zL,Hr 0l$2?.$`$4w/{Get5eB析Nٟ}_/(,ZB5Hxהۋb)22^{}ά0`9~(B{rOml%$7$p=#'8:ǔٝgw_Z'U(tF欃2nށ!:@˲ZDW/?Vb- P c%^Kz %AfIii(|ߘ)Qo+,TX^+`&$s>{~%qeƎYR^q%8Mss %ccm޴AƆQa `\%|G̈́f`D5#cKG%In^p0%)SedYӣQX\c#^(@MAK@1C@gH ǣC_Kz XL@/%݌ˎO}R=Դ$۷&х̗#/^r}ǏB BW%'U"}GuuGcލ& |Yq3K8=f\Ի1WU  z;w6rߞgV&/YBeEҺԸܣy]\jjkEi-Ъ^7DTTB^H8΋ vHIϫf%V\qC8rd$l9-rRObnzzxׁv{MLjfg*`x!!!zz|%0RjS#U ./_X__߻wotC/ZuRrIFEJ"oB育h` >H pK0 0<}_mNK$SSS33=K7T<ǐfՔwk*01Ri…ڮ i$2ME;#r TB08 %qqqB֭[6m-**4h!ӧǏ~lll˿;3fLmmٳ=<<Ο?y'OWUUEDD̘1#&&&33sʕ|>?>>޽{ƍkhh2dX, NNN۷o裏z\?|HQABPfD|ΥuCmЈT#]4{i+B@ݎƎ~܃o333ׯߍ7XBamm秬y<ޙ3g蒪*++!$%%W{{;'|Bo鏶lBYlWPP@m677 BBbb"!$;;.%P.ȳh"B˗Y!ik$(BBBW]uB/y_7GȿƯ z)Ovv6|>+<==Basvv*޽{*}NNNxHҶ~ĈL_<<<{[[[ӛ_~ %%%LawAח.BimۯL9_#`KutecWjLt0e@p:6lH$>>> 6m֭[>|HZ^^~…>sU\kjjF311qssfV1miiad 2.k׮vBӧo޸ӱi$`wzr OgtزYIe^xᅆ 'O߲+**^z+WP5bXF*|l;.($&&'O$l߾4::Z_`tO;cu?.?jJ!Cߟ{(:uss{YH.(ݻӧOqϞ=Ǿ/a|exY..%lh`۶m׮] c٫Gӕk iii ӣ[nر(##Ν;'OH$qƁ]PH>}Æ ;<} .tAp'ы2@ظqCBCCٳ~a߾}NIIa٫AAA~~~}i1}AIDAThhx`ނ bq\\X,?~eMMX,vwwyΝ;ѣ˖- {LPheeeaa!=gϦׯߜ9s{>ecRiuuH$:u[{tA!ёؔojhkk[l?oee+DcDC5$:aMx(}K.C/p?u{4RRZ$ 7fJ q$O;3) {c,,* ^Dd9Iaݽwho원u};ctm~YX\,)yXHp9cXsZLUƐr#/K~5eLN:dpcS !C?zSo,sn iZ,F^|OQU]ty]:{cգ1G O]7 0XRS[K;:>hnigwtAA2:$'@Kte( BkUBwBף$7/o`XW'")Kgr) vIF0\dيQ~c>M[G͒:eʶL^߾3kz;LFALexPeqΪ{8Y;Z$[iIFBX$8hk${xi_Л*++ }PtJ̰5{j|HKKiה^9rz TZk}ew*jSrIƲF<_]w(G$"iD'FqƮ8f㐒W6ҁv4?laYi~t-n+ήaT} $үN5,)/ ֆaڴi2/n~T|*,,ֶW^Ν;wS?sbbD"aҒ2l0 GGLjJ۷oϟ?ӳG/ٳ޽2"ٍ=m" !))),Fr`ܸqNNN˗/g2Se8^u2*1X'}+Sh3+~7Fr޽q5442D,ohh}wǏ>8q"88XY]6y丸> //>۽{7ikkk@@@qqq\\P(u֦M|}} D)// 5ksO}~#ƪ*b S/UږDx")(pÃX\MqiNd>|IU#BBB_gff3F  6LQ0uK\SÊ i%.LHHʊ)**2d+++gfnIK/4sLǎΎ;v, bq\\X,?~eMMX,vww߲e !$222###88X$ٳw/c*NM|i.]",\Σ 7o4559s&n)*%0YpΎ\ʚ K%w% s8q"Z; |M[[̙3n,)-]ں_x!un#nݾ6pvɥ6͞Q᡺ȩs>ٳÇM0! M]!{_|QF! <~6ӭ {+B*:4)թPhaNeFUqzMݎ-Hpuu677d333ׯ~CO<3N*.KN9|(zixиd*[CX;zyEj<_z!`BHQqɾnjz7*pEÙc?\)KMyU+!y9SUՉKgޅkm4T o:ɔ92^/7nJ\G..5$]*huaevגWM%^on^TI(󴵮kUBw\`&{᩹E?2 XsS,[q ȑSqK&)KFUJH dj$z)8q OݞL]Ɯ" ΂}f@/ZDe5\z`R#x(յu Ba`qx'Х?^Kz @/@_z @/%#zB )&n]$*K|˒ҥ+|񅄅 F{{vĒEetTS=B9Mhfžٙ;v646;:ur{{{=;g;),tמ^bjM,L>uPɔҜ#|͖]S=+L577 ޽/@2_,}6V'}?^hg:t8W!ն^665Bz9844wnbu~"#GoJ1e2צGd2I_xMLۻSm%AssO;f#H17v2]H5bK%z,Xbkk{}jNcSW\S.tz61dQXn^p0ڑ[Fޑ^jUV)2)膡.ԟY뻜Q$7|}|>wk֮NMg[/N"ue_{uj2)fdFݱ1ɲ|3*Mڅ/y]qO?ݼeO(qK[}MxKYRZީCWZ7@GH~UI26(;;c؟N7 QErA%n⠻VnWVQXNt ૡ^Ҩ? IGD|#;':jM~فfCM8 B8h?ԿyGBBHIS=}_vQI {en} S}eԨKe'H$H= Q4 a7 3&БϜY|, %U.fu>ZpnpT*fkRJe̽+˗t~?VQ`ؤSO?p0Î{(ɓ~ H|`qͰZpPHkk[Ӣn]=J !aR4$kUURZUus|e=eևs+^oRɓ'>cI"Rҋ_:|p6??a ;;9 ǁ211!E> a7 3P;r0zb.n&ZCPu!?jj>}LC}/i*%W&BhZPu! (*?'sn[7EE?yx}a8ɉ06 l AvҵA:c٪vl1䑭t׸FZ[AwAA@ւ ANQ8v3!3ԜH3;nD[ikk\y!f ~-*Mn{}V|ژ>2я^=xw:u4l/3,Z,S rU}!꣨|sţskd44 7 UOWOoa}ib`&ȑBގ~- ~ka6* B .Aa:F\|1sq&q t^D~ue%ȃ-OBJON\h8Q퇔dH1a:>m""T|-TH1Q>d̿)+M᪪kWn n$DAՅ w1tbfZw|L}VDDDyˤeҒݝV]U #;v8RUWW=v`܅X(()|vfHJ^zA_9˜ᾮ'9Qߧ:HcɮӒBgl9W[@?ObD*G^Y}̅Kv/-X4mo@d/l,5fMԜ`!j_#VQYbײ_v6 ȑmC}96CfLq;+kXśxXlBD1SYIJ=SZ6ТX>@h@Յ w##/xyԙb]o'+M{Yt馧$kb;cM txtpv .(B` %7@B@T]p3(.jgd%ACYlyr?ؖ4yJ/`zO)PQ^vFȓ{ rJjkkY9֪&^s B.AZrA(/-mO4 ,6WV&GGOV|-mI!0e%k3?(5,6Ag/I6Zi mzPu!={okd>XlkyY29e%)[iY={1ܭycAuix;b{>k`n$DAՅ xbbSoLqx$[It5njٙFh0$K=2xHJ&=Xlc`n$DAՅ;6ݿ;z#缚+C%yJK'LbyRӣdejmÕc&Nion\}K!L|a[Z9:KC }k@^,_ad~~L}Oj)?jjbP?O1sѰU=U`o=]D_8uBMmQa>liygo_7]P]UIy&: @Bh_0 4O(AՅҢ//bc`::-RWW{3kSfBmm->!țq0>w3$:KW}&8#nzpk72)k֍+ j_ܣTZۋW ĺtyp=ӵr䢂DF^kY8]5ޱwmv虚Ug? !+O{y|Sٌw$'κp1%8ZpA~+KIe?mE B.uܽx?3w#fɤ0,ގ x.&:|/bR[ /諗\ԜHKL{7];q,=幟2ߌ=Ky .!5t4>EDD-7YպKL՘0 ~\rDdmbVMShȮͷB'NkZsD]f5[~'^뚚;y?:##p)Yl}#(hHh Ŋ.u IѺ0ż~ ^x(SorO^usM%+o#}D.OcIL} 5FeG`8#KRJeZj]>z\Trl--ş ssIct-m-=J3]ʄ0zd ɯ dy:˽ۤ%'¾4YiO`Hhp ioCYg1Z/}I5L9TWVz++SfiH}GO,2pýL9W49~QQuiL{[9E%V1v<$&edS2zh2aVp': w?h3r h$FUeV%97>h G\U;vLVpE}+4q)BmRW[K*(ly=p&|IK~ ]cdТYH;m: v>S5'O#r2<]:wl}QH$ U:UnP0xzPuAjބ酸Dwysuq(܋ c}:%']1a{Ju8|PQYunACd߸C<Ͱ +nhk}Lfa#BX%NTo<'?[[1b4U0wQSu/24ERI4ZsԸ_F~jz9׀Ů}{,V 4 u :G,3-ÄP?/p}/%?iʵ@Յk0p %gOg{oNu5ڊ W&nF`nӬEK޿!-kHׯxtd-ET*YEEeU#& w?@@x-8ʓyq))=)UyYo_>ט0IZV[Zf줩ccr2?GATR?jxmj d; & t)3=5.@?7y HI5 ;l i9-6zhgq8R;,!Uî544{t|*5(Csכ*.$&N iVomv~:}ADPL6KtiqlM&0U2 w#{\FDDd#FSf;ijv>1Ư=a׀ gAr% [3+т iOc|?eg7OrNXJ^O:l={Q<6& MG\׀  IC|=:\ رfyYXYpMuaDv c% ?Pkxhon(2{;`xXobp s^3oXآ$ǹ<)ヸ8Awi5UNv7[9(GVڰc}`ipJra; #"c=+.}Ҍ9 L6{^O-/+mA[nn)&NM`͆O0>\cd]d}H}x+VUQ%+V7W95 /?EnhM1vQkw M\|Lofk@vF7 uwȈQܭ "{I`i_ q3=j0"2p/2jG8(^ًuAu Vnޭ,8⻏kNv9*^Tؓ{A|)lh+ #0bMz5=z1c8䄇 /#--w+%m#GAUe_444}dSuWmy|z6 RHqAuaDdpi 55MM~Xf&l5#zG{Zl` VZU0ssȀ !%9]«Ϣ2fS(͉R}3q~/t*+*hjS$gg|t>l: TzxچљT/\CG}h>&qwQ/©ˆʯ7]K,&6i[ekjA]m9Dȓ5>Zqܶ9%vhh;MZۣے^3ݿo4ۼȑ#+ۆsNQmf'_܍7L eY" m^Ɵ>3ٗלm0$SDT954YkSXv @TuaDd^~ Gp5pgO\c̿._mF2I 1q-Xj+f,С~i,@MOIߑ4w|~[Q]"]nUΣx:G[PLvi큣/I+KQBJN;+z%δ{8R]8pݙSZZZ>DHw vKUUUݻNbfn<>Jc$.F@- OzQn xarJ U7myE&d} ~ZGYYO|s/bX(-+b7LV R6Gkm4GEy!O+-+)a<+ j #HuaDV}jlmm[˨Q#N`L6j<`/?Fzٴiӹs_p!Xŋy*( ~>(*.bQs0]YɊr. ie}b[Yk9l%`KIa!Op_uIJJr2;1; 444C44g9p=0qUhceE=ٙ]/:ujll,} ={X3mqq:?`_uaDdj:b Aآ4LW fe?y{' fM·l~u@/2r ;jw(W~G4@|(/c \^vV`L..,JO1bg4OJyvXOcc5@mIϫ«{PԹ >-)}wihhbW(6UFDD+W78G?e|pUq v2eӅAlQҵk³:r_QA~WBGes5Y#-#[?S2T!jA~n7K,60vNxpoiyKI VzXMM%t^h{zdi<vǏ*x#qÇ,(*0- FՕߥK<`hlFs{ȓwpq݄߮.]jcc %Tc8twMՅy׮]|nmjkkaKD"?p[ĤBx˧sm·?pxL.]7XR_SWZi9:߬#-+gis?zB}Lyt-m-=6q?6ްC·mҒP0@GnV&LGOla[_9[ZD+eG{z#0"2„55 ZzgЧ̚- 1/=ǝ[ \$L49L1 1oEZFz.0Úk& w?b8 XMb?S>!4hW={NG p!5Pcղה/_ttqtDN^vS)Iy90;4l}!?g3߾T0#oGVU~۸0P?w >+N9WȬTo^}A[icT>LڻDQ%ՁF6iNn\ˍ!N_C|.xtsnvMuyxA]^? @dgi雭 bXU"uzPQb"öu|ڼED&qe77Wqm|KKHK3#~YNAmRUUI3&߿e푳^'2@hϐR+S=[TH1Q>Աsn;v^1*HObn{S+cT]qjU϶,_m~ϜQSlz-]r%17}]O5Jst_(ֹs%"ʺ{f.\Bu$a߅Ke䪫޾|~ܬ WgJx]As:5 ŕGl W)3>:IMJ=̀N/\CG}Ƶm?&NK~dgXv EDDWYn:wan/^9 %`{ ^/-+54hy#GV 眢!cPe%'35잝ѸZVg1MK`It?A!b(΢Lk m^4pgHLq;SiXƲۡA?~@m Yt{<{[ҫ+B$XC?sGi ⟝?qpݎ2D{/xkAPظ\  sep R/?#c|fM Sg?zv ^r>v ?-+*ReqՃ[7މRS1~ƄI#Nhi#?do}uLvpo7BܻU[VRT[[[WG ryEF=&BB&5tu U?s Tmǹ2+7Yip Dh*8RVRl֘>t& _IM+r{L@Յ/ҲryY141Λ~irXkZCʈ0NۗϝAG{z<]Ar*WY1do11N:-F]LweL◷{L@Յ/rd%0̏j#48JQY'\BՅ "$pq%lp<|$%W_YyP]..vU"\TWUR⺨*q.ey_:w!Los8ZiŮ?bڠKKxym#?.Tv^fСhϐ|Rө,L""" *vr4OJyvXOcc5@mIϫ.DX #UW~k&<;N=SsJo?u;]o+ҵk³@CG#+*j\ePu|'%؎\@[=C%zv04GF6#]9=n-\fܥk. --3o2=A_t:VSS>_B!#⟹;A"5eae{:&:uacly,_>hu~7qvza#w43G{蚚;y?:##p)TQHд3G {CM,=T]`aN:Q8mH?7wI>KH(*N3hq/9xyݿs> #8bxyf{ϭW3d>Y$FOla[_9[Zļ%ʪQ ET1g٪߈D.'PNXi2wݏ,E[>}el"[כ!y9SH åZ}I ]V"61zbVY &G؆+f8h4h0b=H4TNdyӬtEPu!s>y MK-]k j{>(Pw';Wg/1^-GVz?;,ch@@Յ oI|"ғ; =% n1\I FtR3YP\toM;/G^ 4c-̃p xG6T]#Ѧ&%]fޒy"j 5oz[:wFjBkh@@Յ O0מi+:g>oOf#B- tމ> ǽ"r7B'v-&["j[ aڼE|\}/_ a7B'H.-./f"6"ԶCXeoHM8aDm' 7Dzcm}J FZ&1t*o]~#?i%+VϘ͝v ~ A ֥\^={ Nylc?Mp4e%7K0uE5GB0RNP?\j뚚+S~J ]#fe\\SSM\dU-Òq A~@ViUwDDx xI41t9P~VC}bÕ YR̀q A>l٪>scrv೧̞ 2.( <U (>0B-B:b\6T]V0bZ֣!| 3EeAA.AA~K$^@x\Amm  0" T]  U  ?@Յ  Pu! T]  U  ?@Յ  Pu!҈T"" ukgQ<"("#bPIENDB`PKaxD  ))-Pictures/100000000000033100000061319A1C8C.pngPNG  IHDR1ai)IDATx \L':pm]d+ -%D7ٮ,D(E!ڹJI*ho:~c35M̜~=yz<<=)FAD\Q{`;Q#nnndm۶2*V"Ev4Jlud"^x3"P! j2AAAM  B>AA5  &CA!d  #UZQ I Jke-]DGvdG %4:huڏ3w&_ލ;WyQkih?]TC{\BQ]dG *pdqx?  &C#fc|+ݾ2UMt~/>M}$19Z)tZ!\h Anz ^Dž025^M'qȓG):"ޭC2t_ ;у zGc mdpC2◉,X]V};Ivh<#sCa0C&Cksӓf 2E7zՄ2^1Xu-m~CGO}V6 <77$%%3Ƹܜ#2_WUӀ6,3V56ģ;7>VUV(Pu ,-D3~^. F8/ @LrrRE`[&fwA3! d>:& bN.]r|@7xWJ"+aܾڡ|̗/^`lv[(ͫP'_m3/W٘XK_t(/Q5t#'G_ICzP0C@M oF9umFd+!!afxy J/_xްT/Uee[M1J/.8vӓMPzírKTRTTt}[ 6E`ínO!GfPXOppEӧ8il>C$=(!y&C7hզԟQxTpXN1h Q3`WP 6TWXn7VUMkпϝA7d X5D%j/xK\8e{VFힽ4~]ʸۃ8B]Vc [SEkb ` 䁚 ACA % jy2S3VJo浜t:zΜnTxM,'N7aj3i CF.ik}vA8>C$=(!y&C7ɢ4hAIqxLT߷]ʣL O #%(Rz51޵Kykjj89Ӣ.l 1}OzP0C@M o fUT/?\wzEU詨T#۹ ,˾~"v0gJ?[}ap.]襭>C$=(!y&C7<,4:uݷ_`/a_XS]R^Q)?7N]YI' *Nk ---))9kNpnKOg3!d'޸ %-=vA7e*{4Gp35}3PJR<妽~ WrxR.V!I fBBɘl jT Mc ۡ >*D~S۷]Y+߶^_Q3rޚZYi$@:pÎ"g KAC`7dwcB7cls1&lqSf9EdÎJHHhoe_;5\SYQN.&8!I fШN`BxF.RM(555pVoCvDH3ܠr`K) nzZ-f߿yӴ:S헳io4Ąӧ+L6sn/߿GOfn7+R ?t6~ͦͱ^zEO56=~`Muoezji/zY,r졠/۩2w~n)=a6):been=9!hQIzP0CjM4\_3' 2ʌNAoqa! lFM%$$fη^Ҿ}69zx79 ڭHFv r-;q).ƞ8*n./Cal?l?slym^{bFAI`?OqyBh JI~a "$A ]dLqI/>-]!CՄsɄVWU/5#\Uz_~:L7dxa`kd;7fܥlVvmH$))555&H赤~7`_fA餈c~i2edeU4&?iI]m/ 5zʞJʿ98G3_+*xi_w׌/C9!hQIzP0C%MFgh4J7Eo7l0f#OxkuA @k8@ _gBaT~ ,Fٽu!\`O҃҆- ?o ؎< @^N6h)))B|(r}MþR&oYapS8-81:VF˞AtYad|.+S.D׏j I  0=~ Z"A$5ٵ )i?7`Uq\ Fٺ|K_݁֐l-/+jdjalaJuwMOM EhF z9nԠWFVr'norhi)3:vySG@PGpA!ni,^A6 sτ{:ܺ5 mLOH%MV]Us-).qJC4?ͳ[|VJ?~"ismɋmBpe8C [>:Knr"n =97-r\tMu5 "Qc8sNr7L}"\nF[8<ݾfӟ@e"A!?DE$&c3Ͳ#M!|O'Kc8R=EЎ+;ƷZڔy}ܝLp}͘X YsYk"D8zo\BQEUUMnHvq"Z5 3mteWPRYc߬-DFH];ޖR83-X2;<."DZD7PZA!Z@Hd_3a;Ȳ 2 \R c *L1EԝA/nlμA _$&cDBByN'K1c =Cް;we!%k6 >N -up`˹[\Ņʾ·" "dU]jS}vl:4j>M uqJ_ٳqU}g!,l&9).ldpdxw5XCQ~%Oyd:A=lzRCk z˹[,s'].0u<]J?T#Hy깚 ]T҈!*"A4Ww 1;71*-%ABThnݗl7sܸ F\6`$3hnf?pT8'pѣG ,J? >>LQQXEEy!?40*mk63:::8cMF z9݂ ¾k#BRRRA,$q˸ഔ'Ņڵk:zK;8fel*5:yZۦ07j`ʄ򦦦gM޲~f)8٧l҆/W٘@k׋D@g" j2Q,C`s*++?~xdv| cv ffijPQEcхgCe4%^-Q{ VY!2ߝFJ,_wPQd*Ѵ!ff]g2mKܬѷ~*ĬK>nD.Pj2,dlSbRң޲¾ hXUU k0lVw.DKjMF;ˈq.Y]V&rOJF{VFm}U[<ohݔCqFCLGn_ftQۤrgϵMي蘼фVqVWWǫMi8#-ݧ-Yh⦚V鴒?@ʱ&sH̓߿uV> ob@ iii[HHo߾Os][W. 1ZF2>Z_Œ~%Ki\d`/& D>63yLUd|KղHK)S4Eٗҫ].T@4r;"C>~@wb j2rXj[>2x6 8fĘwVEs4䕈D_ ;,- m>|81kJ?p;p RMiӦA>̚5KzNjm۶6޺>˴)mުQ7FrSE__]VggRVPI2zR&04#g~'ai_蕬D1P j2,#ż\mu22&nW|I&{b~~^X&/_RT?^y>"`ք^RDg?iqQ{_ F?lC)&$pɄ,%}e rp/ň}̈́2Uila;tX^ŴM3a=_~K1:;t(XtxV#D]\XR?{V=\Ig\϶9  Gϥ22sR,uUM |ɗ۹\ٗfC1cƌ7NR) ْ#YV&ߓ"" iQA:+S{\1Ϫ}Ÿrrrr|b[-:d'x~/&>rcݺudo@dc[d\GH3b>m=c=-}E0j:rcKSPmڷōgMKydiLHIKo;m=h2N 14!k\OpBiاslym^{bFAI`?OdǼwA?\]]wNRfKb¡F#obC9=K L{r0?3'(p(Pfu?rf R4 b9 O<kh,;=ppl=, [gηflv>doX\kcݏoGG:]?W/"R~h׮ISL,vު "6NB'<YEEwݜA>M޲~!ư8l8o0(Bt~=Eʊ'dKU65o3ӡ\>1:V{5ܻ~3(9 Onr߾9.!nqSft(ۧf9 OE&ɘO%#Mm'[Nk{ooj1q5հ6Hp]voXQ+ss%Ŝ:NixyvKJIO1sm[+)7~G3{┘s8k" :YxSC~֓bFRa9q S&@4<^AIe;.vz+* ,VH1 n^Ju傎`G8lH? "֙1,77`UTTmͶGxU]jS}vl:ȔOcÃ{((zZp~\DézC5Wpi@1 =w{l d GLtinwcB7cls1&lqSfϚJe ; ڙF}+کs)ѯ?|>qn_ٳqU}g^`ݒ^xٶ^{gZ]퐦{[7okxNn.ú~΀ ӌA}#Y\6KcD0%IIҖC@&M :):been=X/ }墧SETu ?v2.zif>eȱbvzZTl f/EϘgվCY6gO]^ayJ?>?kHJJnՄsc FM?畗=~LrA=3tjߡCǢûjD]\XR?QV=ǏPZawI"N Ai JI~p@`no706pş\2f7.&@!jTNm}{Z^QЮ@\_kk~6; |wnbUلЮݺ/sBoqd0ԃtd01<;=n;8Q{`tGaM̐64Ӧ/_HJJ'j5餈c~i2edeU4&s7&u‚rނV2j=spggVs {jb"_,!,^v{.ApٶF-s3`U$+<t:."?P(4eՁF.Vřڻ\:w*8ϵ=0ύ>qNmNO3DD[&kzؾ{#X+(nߦe RXJá$F!#h3cK;(-틓4G:詰p:(̈́oe_=Zx0QyJs71*)zc ~I?  pke"P'pP*ts l2I+k fgP!jkko]N6=50ϜN=72¹ 0{ba 拖_;m/S-Eh3!dE0}[46lAX2}bL/u]'$#[?I0CA A@-]a,n֨$3#)W` 5 |0Qѝs54".l5*&fd8{2gVaB3$ 7_ cJ/5SEgŅ 0ʊϞ;Ta~ٿ>q1obE$ A j2AtSLBy. o_?{#f@a .Txԗ0$7iBy'BGD3/CFmV\DՄ~!ǦͱY[8/'\XЕ1UUed@̶ZEHk3+TuM [BLTBThbtxRLdWY'Qqm5)(̲nf5P'AA5  &Cn$$pznqsAHXA+5VIENDB`PKaxDMHUU-Pictures/10000000000001CE0000004E051708F7.pngPNG  IHDRN&CaIDATx \L[+!]4T^!"Ju$QT*(EJ*"V'$EtP(]}sL4fYgf~^7ڭla;;;r!hSo=vkSX(ۥ}'%r@x0@"pZAR uPjAJ- AE:(҅(kzR ].vR ] !EE:( \A"p>E*#h+eI$!&h&ĹkG&3#ؑyDBy0J-] AIOvaӮi;ښjNEnRK5Dah,ֵK|cƫ7_Oκ7{RѽGACذM+(|ۗ9kx1n*g-#WHRKf&Hm^+x]]U}[鯤ljORSTGv6~VVOR6b|fƃp﷯^ Rfntu٥@@/75q,caA9?~W>f2ÑJ Q+|_1[E~q~S⫫~* SJļՐs|.*@eOE0H2]CsY=(0!)a+($[=v8\4hSTC]XXuT]]$vsd=8'hth%_f}%\/(=z¶r_ly0%dCI ?CKZd)gƿBIKNp '-"HRt=GoGoLHpcCuMuv+IxlgpSt56X%#G*te$VF(T@ljkl\bג䛱WrR(x]n9 L11A^nrn}/}]޾|d{ wgV9oaϞ߹p 1ٲM"HHR[W[ 3 gҕLgKH + RV| 4KL-vDQcoef>7Q?ʿlՒ! Oy\anGi-)5^f|Q^RJԌUV8dnMu8!:oQhx gYOCi|-tWo1u[] eI{{ Ï712|8l$lKKuҡBn 6ɗaM=K e%İMZzQgx RKV0^Jf6{wm{%ex`W$ˠzmC/|(ȅf}-ѓi9yV=^~մ[ʿE ¾g=Dfk\Wha{,Tu%v rB\9m_KJ|Z#f2+3ww؊笇 |- n?hn}#*|fҶ v# oO0XXp194̀@/WY遤*O-ODD[nUGQ׿3KA:$!F̃N;a'`zq54KS谠&Nᕷ-2A d)sf֓ǰkJAiY¼w "@ǒZ`n]}Qr}MuucC0Z6zq;=zZg8 $^y}.^ `rٙ+Di'戕2'Í?z&*U~4!&Nɉ.,s,7/ݼz͋e_J* g&:,NK꘷Ljcx*df<8ߧoMVͶ; AǫL]J{8AhV7bP+UQ119yYښ+ ו|ݝ6}ddظ弑X,I^zTxҒOrDKq !Z׮hn zs)ǺڒS#Ξ_I6cT[Eջ2c38w0ו?OwBtX0jC-0J- 00o$mmhÞ 111}}}===yyv;.hփ f-] Rn‰+M;qˡ̜9{ ku[/%^.!΋0;r촚|~akzRYu.(0W:?o1J-0J-B Dٽ{Zk.|ɻYYY.ig`_DXDd@&#-iCx|~缁B͊,]1m<kN8bPj;HpOs#Gv2[ёmZBkwGA7Dm ڷ[` j.*T+%%Ŏ꪿n]:X<|Qwobm'*֋y;.1U>?2:F&^}<޾|1iTه[} ̀R@EZ\;;;vI^r= tEoiZc]#Xk4^-(F+2aoA4)[`Zpq6;=]Ț 9^{" 1aBMmN+Է$-[fM>?2h,Zr#*~Jb?rB@OQyK7no-Z7XXXڶi &dff2n#..xbjDEE:ĆWmggg}q2D?5eμ~R|x'C~.3/h&;h 'Ne*ZGFz d%^>z.ddž?LMr;~2>RRX^C*++Y6+̓ߣx§kGӷ5}$͚ t՛\m,v~)2ҽi4tbuϖoIXږR6̌tcG ML;\а$ m? >2AjRqQtjb؈ʾ&AT|՞CzǷR;9eHMM{[MšM#ǻSr޽ܰ{y_EzM;joO[=zK~,,Zn4VyD=zZ.;VKBBKXӒq-0 RN~}RZ.~#Mq:$ ,@{fN|&jkg/niH3-r=Ghc'=be* {۹ʌY-Yf zd4@' ^P|FJI@XВ|`!s/ɥOOU_aiZRB_466 (%3:B>} k?++') s1>3AW/)3\3Ro#45%>:9yEZf8R(U?w15!°?Ueŏ!M\TQ@ehk>}v I~t e&fT7^ׄ2 HuUK>n[YLx=IagH4݄h#˲N4=a6gI:,^1BuYE U>?Jzx1& 'l7h~mf>AXn|:\/͓~.,&?'JhkEr#XJZrk@8YmqkB4tñݦ+N,i0Ѧ 4QXXѽ}ӝ:0Pu)7" L1&dFS@`0/&;+z3HPhCEo'ۺ54 #$fl籃{& WoUq/۹3=`6?"f,D1v RTfuu3DwgV9oaϞ߹p4 !$Ť{]$ #Ǝ7lnkr&$κt4Emnt' >: .\n$)%SSSGF',~SH׆cRK^:S~~+39 Q`fD imlh<u[?t*Z:k?EEz:J$hXt27ҦY_W`5pޑQR0Hg>^jfFztX_ =3eiWolv¼.36#Xa5u%arV} H1Eq愻% v ;lgkPҁn ѶɗaMPٗr İ4ΜuuJOP;]4GoGiKٝu%Q#qky%+qw %>vOhdZN^O*߾v5-VǢ0+ ¸wR)lnߵqtLPmN6WuZjQJ W:mp^jmkI O˾P f^̨0f? 4?'uJښdj[Y)lPo/7`; t:Րs ZˈS$Vm`&%+Ѕ/r @br -3\AgV@_(݇r657&3qO~mw~yTd;?+KVOQt2x =l޾|a5u a~^9戊+]"9Z: ~] (* 6}ddظ弑^̒Gg,-$7Xٿ^OĈu(yظaK7#zӮODG[ot@"Sدy4IENDB`PKaxDƶR2R2-Pictures/100000000000034E000000B73AB35E75.pngPNG  IHDRN2IDATx \LiT-B,QEKwmWYCUPF-앒HOΝ;4լg~{>1Ϝy73g#[^^ 6 UMv@MRM,Mdnv+@ph Qh Qh Qh Qh Qh Qh Qh Qh Q@m\6ń^lP:c ;Ȩkk~D~պ>!++ZSKSFkGΝ>~.PTRnڹ[']u/ɩ3D=o߹$ۨss^rß?$˭ˉCGU&/_$fKݶP^^yݹY I\#KgOM6g:hx @%Ğ&q^ބS8ُw9Džڟt6~%DIqQnΛK'^ahĶIx$5mhi?hqKU5o_>y⹘"C:tmiSS ^+D=ӧ3fܸqM6~GPQ0dRRRlHNNJ-|N5nVÔ9Zsw%ɒmIZ=}3 Z$^:{A5B8}t@@ɓ2Q'%aM~[)iir6mIV;w*z悪[kMi 2_qQX:wAvꪯөՄs V+aG7-ٖMDUx[ە(5Z Qʅ3߿ k,nfqh6Ḽjݼk ӇQպɏĸȈc]s;GF[X9s-1,we"qZwX!e>Vx>ꤔ0 ̒ac{{^W#̧LP'-z5>fFݸx!Eڳr&#'LLQWk`B $c G8qazדm~Uԙ-TTtJ9 --Ӻm;Y7]@9ʞåh:,\FK~|?4ezjtۗ=vrEk HTrAAAMR y k~FhERpƺ>ݪ9v$_6Wr E%e!޼zgm\65C>y£%DpX!!¥2#!%%nوЁƣTu{.q2ޭk@k ǧtƕ8`G)q]no;)`ݛ m\W<ھoWjw Ek5Awcް}ԜD 'y/f<yنŅߺ&{~H]^*ox l792b&<ʗ7/%+4?tD+ 6n\ńyw%ݗ:LJ95hpCV^LXNv֘ (TiܸIY/R/G0ezM76z q?JJ6"s4=zLMC/RU'v~[eLz}4YnD^~5 RS  QޚV-e8s_OjnfG\I<u=|޻ZQUoӽO_vk$^k`b'8u]W!YJqȄjj\q=Y꽗:m!m!m!m!m!m!m!m!m!m!0)7v$B#%%{[@gz 4ׯvhQh Qh Qh Qh Qh Qh Qh Qh Qh Qh Qh Qh Qh QfÆ nruupŎ{ӄ+DV:lvZDz Kz@[z@[z@[z@[Z8 `훅 ڪT10A^W7mVa@0`7.m!kؙn#9֚4t4ڛXLnݡcS뷣zln@z ~H' wd +9*.^=zcu"9}M3Qq!/ WP`[޻'nPoz 7Љ,}:iH?s[4I{PMuuGcg-:X;}|FεJQYi^/i iwuk~7o_'RU-{7SNz7tISX :;ܜZ:d16=AG\MvIqZM=C?NxRuq\ۗVC ICrqEO^yg[dW708vv!jbC6"w5Wu~̻7/&>zYjʋ ɨqܟ?Rw_&KGP/=!KR\ ˬs%dgLO%KҙHHMO?m.*:y`tX D\CpJϠءi՟b RRRSg2.mCn(+[X\\o۠i.LT=N1޲!()4}U+Ez\>d9g:N]*csړ=IJ&OKHqEct0 'U@,7}QC7ྦྷIr7@\teT|:Gƒg,vJz<ʔ ?~}/ꮆ\/ϝ:N~ Qk%w:s*vBͫ MohϕAKֻ;ٚzV{&?YOQE7@`Hv7@\TU'V(2b-l$'7e_u4kNgɿۡcƳ3lx2߹z9VUVΕB&E7dvedz ^ge0*?h#hkͶl坺sY3Zk#+fj'~yI_?$Ğc~ii)7)u/ n >n վS~6WjVF[f+Qh֜~~a L?̴~~/J؋Xn >n u/rա1id|Ryr炖TYK~. JVEM=';xRr抜 cZiN˻!QF222v Rӏ hL, C"pbޭk6j4pjWhJs߲OsW/x2R53*O'w_ftn5;|:w,"ĥhcߚ"++ZSKSF*$jS$i7V-!Q9&qezK gO//+#5lbnu)>6*CFk?l&+}5{(e354sw2fs9F^pc2 3Ӟ?}=?ʬJS}G{RoNstvk\ӆ 6y#>d [x ;Ѥ 1e #Y-gRqz6/Wda]GϠ̻3<-x6,Tz#}<0k+Y;])ɷvQ^Mܻ}Q]7@!jkՏHG=6O[S,Rj5!?JJtӗ5XPTRԥ}.+߹zef{bd?wbh2227/--52|ĸȔ|ܭ&&,Wh#b񸐃>OHWPbbfbaZ1~<tMKhݼŭ[OLL_:ymړGdsv܋n}`T !)1 46!Q/3)bBg _f,XFޱzcv{&7);v&;}g޴tp`j5~LڭQuRﴸDRV&3>r{෺l%=ܪk!yS}kmqda+h;4k wo/CMh =@ԈW˨3q+NrueKMV&.[D书*޻yyfi O^h}*y>iw& ޿ͭKY'8L{\D_ړ2 `A}!  AXF3QyQn=/!O?H,/+嶪٥^w6nd*.{Z k3ٙޛצ>N!Ɍz[^AQ .'gT^ORRӜiڤWM G ue -YKViw%#ٱ'Ν>NkeWk @'b=)()J8vQ'Cf'_|32$ >~A^{鉺2'D}/XFv7{ekID|dx6AZޝDܕVϝJdo\6!:TAC7!Qm-7h.I7y8٘7p5\I Tb~ozW.Q8NǤO"~ƕ}N`-$(ꃆDV[>A7.^xmqQZ -6q:G\HGjAw^lmXZKEM] lP3qOvs98UjkN_֣ ʔUZu&K]7Ĺtn#CYyy9vn,P5Q 6VnC>e`A5SBQg ?&~qjnrxBd=D:걒r^lD@Q}ѽ^nDnz^F1i7+h$!6Qno;)`ݛ eO>RUP2ɑ!GzuM$~L$ZCô=/f<yن]nʜVo$o[U{Bun'7Y/z#K:tO-Z!fQOFFfzbN 4612*?&+sT =}r")'OP,0i7?y*&LRh<%]JJ{08qS!!jן;|!|ᦰ!.C̢I;#PQI9%VLXry.뙫-X!r{b%&~q5un/Y5n"ߘZ=zccbiiU?}'fEE=b@ݕ ̻6V^^>g:ox?mrI':[~~J'&L]IGMs&"4QJ~[v::bҴ~1V\]) ޿ߪ/aPo+}Kum~킂MRUQae={8*l 1S'O aaa۷ɕ:u$; ôL !dWHD2e1>{QiŊFFF| j~W%''!C%!x%&^D2ߋgO"C}'--Zm[q/S߽yEi”U7W'L߾|jHjsp ,,o_ >)j?a\cr&MyIK]}Jk֬ݻ2_Qa݄/3r;?sFؤ;(Q:ypo펝KI7#i):mf-0Pq>Awٺk8WmKkAG\MvIqZM=C?/*??߯ =2X.~ 2ӽ\g۰gi.<%.Vn޹$lG$i-^u:EElfnˈi8}[,!oEȒt&DXdbԯDn1l$sL'ڪ={$ z/Xƨ-$獶sTk}ޙ!'uhuu)5n~9~lL'PY~ORRӜiڤW؝a>=dtܪzyyI碃}w_HYg`2_n=/!eAReeV::t^d*\ׯ[֚XXYOU޼6q g$M>,U?U7Ir~9+t|F_ͤrwOj51W|Bgf̊g[Ϥ>mȨr΢f}CWRRL"ݢZ $&E :R׫$PS|@ fp(I TXfJV-~|?9 Q%mΜ φP;Mr|rJȐs;SGO6ԙuD.zqi*#ö\F޼z)tFGlD7%ݝlXO~UmVc$pnnn;I'K,quu&={LIINfƍG$3F^^~Æ h#bzk\PPy-[.1|FdLCnYj:::ԧ;}dž$᭝?R| g4ߠ_}^('7=^Lm*e~WZ:w?TfUT:+ΟHW15stQ*˞uUT>faFp=>*uFI^ҡN. (n^[݇u۳' iJ];CꇓNx]nʜVo$f߷Z۪=5nk.q2Zt[JJ~C,K§z7~._,:s眼$͋ #,mI83׃)|x*..NC;&+sFC${DR^׫p$<ңCF[~WViŏ>#=3TUL}vxBf5dg 7d+׸+kk)Su ٣ y0otwPg;[漹{/yahD_wQ8rxޝĈУP,7{k9pӹ8=wc# s9\\پ#OS9٘Q%mko$ꕗs8KEMi[?Kfy].M%ۼ\]Fus=>VY3!演[1a-湬g.XdR>؄Z^կ$$~0*_qgx[~l YU6Gxwk+YȮE_٣~yilaʜEJ-U&ĜjxԫqywFFA#Co^Lx-QZ(˩ڴ&L=о̴gyosZkk=cvG~8d.߰C^ge`ױK7Τ&M*~IpҐHSKLDh꣔t7tĤi(F 9ty -!&f&V"$lrX-'C.?౉c# Ả2@=y%ʏĸȈcb>링);*V3d52.?yؐ)=e`hZzy3Ƽˏ뤁ݩcn)ɷ8kU5Kw;l5z7Zr7^gorOF^' N$᭝?R| g4ߠ_}Ioz7XE(//_5ۍ< o'#vN]9tD)vx թ [`7Zy'G?:B݉;GgL~ʅ3$zoZDĝdz7ZhjgMkŲuDNF㥡C3avl<tQ$'{-ahq7a،O}=7/۰Y.y؝\]qeCFG~4C2捦%ݗ:LJ95؄z!yޝ7>o67Rm5 RǮ}-jV-}KvPa;#  =ABHrgüђ~ ̻ 꾸fN-[:.ZIZ b絿O;L󣤄*g=WV׻'[`\²i=O=<X'}}[ 5iOh[.+*~ ߜhq 9_g j7`'d@zUTH@|dxoḼ,03\c$E-ݰ}T!LqQɣ:@ٓ~q~܃|GM4vf[7R 0@T! =wtБIʚ+)[\0zJ+QpFS9y\Ts?J'MC2D+Ez\SYيۢ1ǏZzmu:,}ˢBƝ ?t.EIc=$ϟ?/#{F5Hͧ80?bqK'.JNΜ $fTNdf=~[%$ͭf̏~ʜEvsYv1>!}Jr -P2xXzCA0@!}(T!w^0k }||sE%RW9yoB $n@O{)ltd?snV~3KͿu<)isEng^棪Q'O;+Fu'L>t*1  >ɍ?,n]##+I;{=Y uͶd~ѥA+)!:,0H7hA -Pictures/1000000000000374000002304F2FF377.pngPNG  IHDRt0AL"IDATx ssD+++_uɰ\Ud0ԺΝ?xmW/y\)iNr 8s5Kl_??{~fy%+&ѝ[nX*eeei)DD]u-PYYoK$ )+g,XqM4Bv3&vK #,-)qzWBɋ'J23~РV RfKYN.>հs*=zM10Y _!\=/_>uTv5vv3^* 䆰07++wSxL'zjK6muU1ojβmmHe[A 2nTkftd8XG!6BzJII6Mʌ}gՙwG׫'p~#&pVU{u)F#O,ˡ_@Bh]%551bǏ\OFZ˧W`v2b)I khFDk7g54l$&v֓)/kϣIRC>s?i'M4~Ah]*++yTLnn%rĉzzz:uj殇\ ˳j=H]zcuy-LIm^HI ;YY}~)G!\ў))7Â8,p3umڈеEꃾV;[^ 5aźkwY/1y~%ЏG部ϟ>5n[GKG߻SiPn#{ );gYwUAEVk;K 3%ӵ-OUpy#ᒘmǿ“ƹ:[C>LO[PMzkY卯.'N2e O3%[^o0bZ>fBIo~w"""kwڮ3s3 :|h:uI?!ҝe۶mK;"nfּ?5%Ĥi6t+"dݟ (&.>zҴ+A~/ހ^Ly;qb63jh z2.jخ}2jjn| |p zHwjN =."\WZFZq[-vG#ǜur[׳>e|UYYEAINA M%5%:駥6Pc!Im;>ARjz \m.6@K %hp A \m.6@K %hp A \m.6@K'v"nB".҆MFXX[tBٹs'4C \m.6@K %hp A \m.6@K %hp A \m.6@K %hp A \m.ov&R666noV:߭iݲ@~eMh]ډ?v߭๢ !.ѩ[~hp A \m.6@K %h#+dK\/nPvSu0hbMy ͊BPH^v@Khkf+&.覬P鮣oUGˇG7lC XZ$t)Lv}#eV2].:=iiYA/Kh֘;⢏>{-合@w`[Wn` ۶SыLC7g2ͫ'%o~~76 /`P_ xT{ꥀf.3n{-"!UEY ֫c>OAw_m׾ ,'uN{6NDDF_M>gki'$yAYE,3@}۟JZs/'7{z8PXp3˭|3MQ0{\(*~!-60q{xR!\@ꡮ1xAйSݣ5`;7_p .,(>"UZTyqa!%YgQ^^N?LȮf3gSw߽%SdDȺLά0/Z3}wg$Ɠ)rw0C;翔hsQхn^+ 5Ap JCkйS) GN֛#+n˚ByEE=i" *ٙn]u;zRęɞMiIt((~ʊ|x禁Ùtq ;>mܡKmb_7)1\hA a疮:~,,'о-%*^}Ƽш@24s5TaWMc⠬7w*ZX}ubu'w.^aj=+[YQuE)%:lcU(6d;m-Mt<׍˭iC_rt7[\_ 5A p |άѷ/NgZ(&.>an꯹0]߼&3ٖ?u&>{uBŹ oRsI'辇Շ$C;>]Tdte+WkF_wWN}YIy7/ݿu-3#=KvYY7GO?@#O}%*j\kj}42V.e]J$:Jy<2a/9KLRq]Vo")%݀@pxB{x .@P=}ߡ$ٹ|˓ԙ15x¶'[f7i)I8S2vȥwB.'qhuv=1 KKJιr+a-~ "sA<{x/}Ae]:5z4먒쬏dΖ3O: WVVP'SqQ!s<M'辇תx4]0iUYYEoN5X [u;{~/mY5'[tU "3CJbo^PYQAvFKVla=$+7vʌR2 /Oe׹\zjr*ȞfKٙ[U~կL{Eݸb}bj䄷/q;ZVZ YC{B+s,'Dni׃1&vb`\\]F [ީWj\CǃYKPBDfF:=Fh$bmWVJ|gH~nhZ\V6b 猋=B)N,fԹ]!VXܿiյ@r%(^kchcy%Lhh Yz#bn_ #U"p&Şz9 &- ЫOq}a:†i UǼe;vᲙs*-)n2ޚA HI˨R[IS5ya<|'wo$()5fk%zYY8u+"8o_{0w I8}Rb{ e::F:' >t. ,Zɉor^}b_ՙ_szu<=t4VgP'$yU?C|3ңQ0\(*ڳϛY>}JO+).Wbhhi]ܨxzH #'/a;M {۴1Ymgc\A+ΟddɎζ>CLlTx_b-!C ۊh>{x=J;.ʄUiፋN9H 5,L~f\ T+**|Ւׂx~u~|e K}>)QkH^ډ|j|{@;J;.o]돑z`b-yrmMBgeEӮ-E{ikc[Ѫ>S\ݹiIǿ!Y -v lmJK,\gEAKVVPW;7 .#ۅZRz+E-]uYm?PQ<]H9pTHuGݝlmw;nrIڵkAԹGȒ~ǩ_>rBh<9>GYEm-i罯^ N72A^#HᲴ$#-VDpйSBտc>4{wbz>g^?:b%koDKBVnEPc6%&ԅ+~&ܯs0_*Q`t%w++*N8] :,%ךe>D { ^De6z :JImYٓ?M;Z'_0qh {#vu05Fg%Yd^{Kc]yQ똓 TF^ibzWgaد *Md+?u&nOޮj.ֵ/UzV|QUۚ6/BhpypO䕔Uf FBӬen~lr JlwCc;+𣁭iC%**9!5WNќڂ)y;%y7/ݿu-3#=KvYYYyy-͠Stm3.Vd4R:I Nf[|m{ɒ2lβ_sQcud|hZ4:sX/1f򄪇Mf.Y [oi{%oԄCFc(%MX OWiaNnPxrbOc~ZC$;ζtٙr#-hJ?L$MYz=|dlϣ/xtw8~a +ni)IdK~"HeZ;-@A l?6a݇ENTVTXnCMmY׮-GB۶k.>Zա^= W.x=i5Pdg}>*Z}YXX [To^>:4qfQt*?␞Dj<))嶽'ľ9>f_7ĸɧge::FdlbԙOO4]< +Rr[y&ϚPQ*&;r7_M&߻Dqq1m۶%%͕czKHHo$?܎/Rc_[avG*Yt yPN3 Ipɼ{:Vرcedd l5_:5I$e{,;I!r#Oil.%g&@S?'˨5kš1-oyFI Gs=W~z~ǛI!C :KezOi%EJ -5ߜ;ssx?&K lûj3I޳ZRVV,)Hx0"}]Rw'% WomN(.*p-?# 9a1.~u~]2;lfG1"#B>8f6)IR}7.2:} I~Zzj2 -$uϣw^.eW!~]kOb%|"S`Itf]XP:ssx?K lû*Sd=giMd^>}|]Wn%T5|U$YN107]$9 ^<:6qrzm \-]uYԘ i)v',fѓk部s'$ľs?N kElmJKIu((~ʊ|x禁ed4ѭXK۴pڵpu/M;߮?Ww|eFZrcH¥B 25羫!k@é6PUuቤXW};7ؙap eʗ\5~ \$c:L{II1 8Vэ:oE_>S=M3˯KJHPJ&L(%megO~4ܯs0_*Q`t%w++*N8]otU|R&1#ޝz.y}Eb-6z|̻o^\Qz?~r6/Ce\32>IVnʵ:zF2_s> ոd($KN.) Ua/YY̓p; y X3%/ >kN62jL#k΀tYKLRչuA`Ȕ9%S"Qr5S߉q{02%糜TEM~/V3?8d2R"cWLt?޹sgر4ni2%c$Yv&sceՀR90.$Y+1Xn5DRy)EDDf Ug[R;ssx?l.2%i|ϔ5)IwP\>CKe ޳ˏޓkF [_ .dXѕRkk8gJJZZɍZegJї|<:;_0s5GE)^tϞC. `z S:ssx?vnO~^BC~7'? yoLIlDEE* d=x(:͚[# OLDYl;k].~֨}YR#"#+5YMcx@ ,,,L^5TͿ3Yt%eٓ{i|ȿ:uTح-OTT8[yɧ[2=}s]i#I$&&K!S2m9.K;`[v@ϪI-3|i78[t|nj֮}{AԻ r^Z߳/t~^advۍРNrg͑(Iv JJ5CPufݓ޾x|hYi|jEvs}ͧOfz +NufFϟq%o7M|fw'wR1116&OD}hFIr2],6{3 NkWܾ؁?{Y޽w_Sm#_kchcy%Lkh YzcC>Y24q>^P4&QL;+&ъvF}46d~?-vر02Q%r Jg<\Its<@&}wf޽68Ç#lC6^iI up=,UǼe;va# :xxO*I2T sɳwxr›nj]R'I $JTgjaE*l׮׾S/FɆ*j_dg}T븉sA 3x@KbI%$U$߉Sx+*,$"ȟlI[jI$,u޷=! wSgV_>"%-3hHk((γ"''g{whis.LޚI"܏%Ľj#,Uҕ#n w<\Xk>4VgUR V_wG~W絶 %3OFJ -l}iLwbDA›7998"Ԋ;q ]n.ED*w޿]G ʌ$}_ǐ'<݆ -TpWa疮:~ڬ$TE[NO\l^UCv%ǺK}dk׏+apFO֮]Ν>yhޮgl߉Sq6H;ܠ-\Rg =džmKL8 c_rkos{ߧa=u OK{N[K] H?J&L(%megOռV$#-VDpйSB?d>4{wbz>g9#f_4mkdwJKl uRTLlx9G\aNl7ALֳP`ZT߫\&[Z$T"ɽjCf.6l/ܼ.)fאgyWlkoiyQ똓 Muݩg~1яNڳt6Y9yP'l5@Rw|3-KJwֻe**9R߫+qӒܛߺ%TrʵUM728\NAinXAn(+djdw"r^ s㲺@#?dX+#@8ߌCˀ:-^{{I):cĤX'T=:kX -L_>}ܶDdIVUלl2eԘjfFv^}5O^}˸gd?}kuo.@KTw|3-=Q<\s-X`INbbb"""3.&ݩsv'9\/koc Q&12g9dQJ=92ǯnN$J.&Szjr;Q7.$w{iI uqjw|3SI;"\j=%m\z,+;-t &,=>wqpy%r/2 9a:FG t:yh%3u˳e-ٲe󮜜\7 ZO[,.*d^قW(TY O::t$%ZCGyzH x"oܺaK,:"wqqw.=C{B+s,'Dn$_6DmFfκ $+7y1O9ٕky-ڝ 8jSg\SLNOGwnr4ioImۜYvj薝1гj}5io?gw;|}>7:j^[9!\ɖLB<ڽbn_ #U"p&Ş9@KQ൲2-kd U_"˵BΞߋ]>4:^њ¥嶽'ľaIOYQ^~tzj =4]< +Rr[|*j_dg}T븉sA]Gk1Xs5҃ڐN{see岍;Xѓ`H=A iN?t@7?($T)PKȑOUf-f ֜v.kOٿ[9y\s 3VM%Bi^]tJ̻Cnպ9-n/S.rVo"SRQQ~kϝ.*-)Y@/ha&iӪ?`xiNSꥀP?̌y9(6gppq`]XTIFZ#o^M4O509mg4N[G~an߹QTB VJ:FGex%:"Kh>~xy-8{qOv؎6Ͻ{KȈu<*x*.*p-k;x傗`w'[oWɳ2YJ{#)A+p -ЫgO.yyBRZf3ΗB=(T. SUr)-)p]deE^ qu>s@FEEl [niIX+ׇ5bD=EM64,@Z;#=uPzMeHXuuS%dneE M"՗;wbJH -^q-VƹBJLN&oWg]9t<VrtMvOޞmtkAќxI)iR#\8 /.Bhx.7JhAnRꪢoxk/+AR!ijU\Tx=s?~x/T}YK[[gX!\@!&.>il2={tGEuFfdbp]]>>-%IV+p./+ F3]5tB`p -Рa#!%)fX.nt +wSmdE#R~y^փ.\ uji+q*=5!A%%ڷ'Adɂ:Of @Z0j[v-XrWxOD_7 >"wqqw.zBn\߾^l1br-+-^Ϲeu*+*f̝?`._qB3%pd9gd|KgSU1яܝl$T=Dkmm#ua !Vo~HJ)Ap ىR#4m{mO}Ejփ*>f_7ĸKH(7sM]Z0t)~?iI}&4%՞;37z)` ɛaA^H{UQV1H{=O-;Sm۵|1;{]=S޽qϙZFZ#o^gf|PVQ#L509.yyr"y% j.m=r+i}/;# _zA˳ P/ zk4^tԳGpYөy3>}3 g‚CE,gEyy9U2%ƾb+%f3 wobn13+LJfIJb<"/;{3TsKʏ6]8ŹBBA5DܩϘL#RXX`zsdb_;mYS\TH պhcqqq]'M3\dU%;ӭ!nG^ 8Ӡ6lmJK,\gEAKVVPW;7 I}4.۸Czub]%ľ$oRbЂL5rE-]uYAYp JWok}4"Ї'͜x5U2@{iFӘ#9(+-%r~Gꮲշܯ^W/pt֝ܝx2 neE Mߧa}W'4ѭ$K."ܦ ~ѝ;_lq \,@93k}˪3'ϳrرk HL|ܦd[fԙ$={.m]kZggqn[\!\$T}LGsr"+[y\_lHEF72R0Zֳyro_ZfFz첲2nw+B?6 %*j@uj}42V.e]J$:Jy<2#/9KLRq]Vo")%݀գ;Je$|˓ԙ-\G,Tr7-%Iz^?{J7YLL \}Jn.XUGt3)>=1 KKJιr+a-~ "sA<{x/}Ae]:5z4먒쬏dΖqO: WVVPm;SqQ!s !L \ߛW.TVThd3ZxXNrc(%_vȥ&׹-1qb٪oDA韈 U5W]tQWԍ+-fvONx}e U1T'Ľ80wrqOHt薑z=8pjt)Ib`\\]F [h&.Yg#,,w~a8n.q'%PVQoZ3+/zNOM! #M颠8ŠV^-u޷=!Y9:[h&.b bÖnd*a^rW+22L6*+'Oam}> ZB!w߆mO \@˔}RְlX*-.e<ekC"i罯^ N72wZ&Kh,6j1?=5yl m?!\@Uwpxσ7>X\T$6|аn@p -lS +2wE8.6@Ksiii 0 f 5**o߾Ր2 %[\\jH) \ 2p t2"hRТ{,;S6mG4\Jbo^z)=YFGYjڽjS[8mRQ0\(*~!}<޼|N *GN10_".ޖTGH23>(?@Z믿vţY}!IM9h&.xՒ2fIA›79ԙ*uu[VZJ}Hn,\qnTm;2/.*p-ߋ@v*++$>~u~|I¨". dYn]Ʀ=:**SLi׮k@ \@y0Is.6DVN'IOMvu^eY&;+~,.WRuSG~8EAk6**ZFZT=,u[Oն]{Ro}mX=iZv>:wHBK?+בer)-)&m3ꢠ%++j᝛jB53%Nr%#s%AJQ1c&tݹzItj /b"WoK(2-&VDE37Y iC_rt7[,H",)1\hALQJn˚ϞhYo_.^aj=UJVVTzp|d?M \@ѳoG'Y~nLֻOT}w<~,a}u֚:'wo7pL ?p -ɔW,Z5j 5lD!DDDdfTTdtc[WApPVQcIrVD+ (UCs3cƌ 6 S \@ѫ˗ϐpLOtrלdqU%[f-%:tʗ,|nrn4kТ(ʚLDݸcKKJfQ df|YNAuE*tv"W& Z.,,zOy2eVBFZj@dtI)n:Z&e52ΝuОKgp c{p{2WϷmڈTTQY>)tW$2-pJeZJzzu%6 bӟMYcp uƀGghc]w ].#+5YMcxC2 D8umu]A<{x/}Au]:5z4j"p -x۶9*.*;`[v@Ͽ~HI-3|i78[t|nj֮}{AԻ r^Z߳/t~^.02pFhP'YɳHt~xɮXsI@Bmԍ+-fvONx}e y$%&K->!??:!ۨ2],6{3 lYWܾ؁?{Y޽w_Sm9XG^ %ZCؐg. yt@7?($T)PK0~J(TҢ/!K׾$6%k_seoPFE$reRYJhuܙi45sywy˼3{Μ}@K %hp A \m.6@KSI|BFV6 䁕~dBQii?}dT"ϥ"Nqކ %RrW@>|,4r n{}>g%%Ty$-1Z5~ R P.s#编 %?}zyV%+xx.m$';)dj]{106|)05PsAQ^v\]T=4iƮ5˔UՍ,mMe[ \@@ߤ77679괆y拧dXeψ -Z⢢Г^~eĄy_v>aVU5ho$ \@97*+XYdd1SA=oW͞O&E ?xy$W)77bdM ԣVڬYmQӧ\7z-eHv융!"dth`xoDߐQ㌭tL P \@QVVvR4SSu vNzlavUBVJVWvp6jbR(!)I?Z4gP37%Sw~7И \@ao8ûnΜ븉P&i ye`V>{ׯRᒉsN&,m['ń{hF_D*YΘz2uK̀yJ4yWH pԃ4"t(Sd{ׯN;l>{|jpFNwԐI'%?bTb^%4> !lךe&6 MgAQY%';t|b6rOUqQ᥈г}<ԭGM4Bp Mt㍦$bM?{#ېIm;רAev}t\I`O&svNA&dz2}  @ \@ S'K2%CzLL ./^l&@C \m.6@K %hp A \m.6@K %hp A \m.6@K %̖-[ bI@p i Ҳd=_/F$ӹ%hp A \m.6@fEe q#%hBJ+R\wWl&ѲdC@U|QЭh74svBn`G ݊&Kʟq!2VG]w6t+D6Xk;rK %>n޼zG5MmuN&fZ>yVTA Kh~zN&wx }sy7tt6 D%4&wo=BDtH`kʲYU4 nЈ \@мEK]4Qg۵f髗^v,\d D%4>Z]9p[ikzѓcTs娰cӞ$?pb.m ~]G-Zp.W?SRc_<{"%%յW_F<}k9YG?MyZMSc`j][}rĽW߽.).RVU;~ጏ 7ǝn#o_ G $@PIZ.E~MxDj\ݠ> ZXvA(urVةcܠRc f>}G߾1q]*/.,$9`eeeTȔĹHAJkwFg\_ ㍦/wJrn"vJ*1{>{\ycsBg_q?snL6&"i ze`6h#ɿw*s˓]U5>w_ s&Qta3%4V^2&q}5c6[yמ:|y&\SU־̀su_>9o?},--Ym¨ k,@h}!D %4VHښr}'F-[WV"ۺ y<~u@Z<9Vٙ/\/+Bi#'_4j0IJHAεȣfgU6dK|vrKA~J%_ ɣyTTV:تz|UjtPTdh1'aխYhZ~?q&o""ܲa6kVgt'Eң7GI)|%5%x+++{A;׮hڃRgO&eܼm}sY I@$SӔSf}#UԿ&2ӟr$7YXǞ:}*8BZcn^%ݼ[יQQS_ѡ5T^zllm^]SFڳGwox(̪5;=I9bar'˶jAcNv֥IӭI ":$peqwE|4n`ptht.v.?Gb݂@Z̬w?JJr\|p|LOJ"9oȆ ϸȰ =Jra6r6sF!_o_ ޼ʼz1lN}5;w%_յ5q= !!1nc-[Pwd : ?MԲqӎՋcCXIWdi#db3ػl\u5<ˉ>$Xx$MJLOki{70ua U{p s)"dRd(3ǥ7WeaP^ǎ?H= ُ %B5#Kf#D||NuΐHJŎ1$\f<VNݓCKwKM412zB;E5׿-_BRjb!Ӟ$G5  20L}$U- `FLLms$ϐr4u py9z$!%s>߱Ϝ ;Pmߣ$cj3vJo_ ޼ʼz1*@_]y-54%WDu>J7oN>Q1aAW/DZހ[ۅyx@f~;`N%Os22sWyj Pl_vxLo߿}[9GR❨`?9 71 ےxwl^â'\^id9wqthP򇄄? \Q~B[z8%q4;ϘT]N$˲-r+>;hPm;2Vnv#ֿA2dH-8M![r;Ř؈AKb7dgw|CGpB+^ʧUTTT 4̺MMCTֿtȑɓ'wرgdd$//K@SN۷"ڶhb„ "2_~H 5; oj;.d~Ҥ94c`j]GDhyzz&$$ٓ.Z$eS_g\vdܶlw8`q2 n}=\HoM穞IvQjFׯ2P}eB܅N6a549涛$7͊l>˹ŀT7E~dg];u<5ca߸Wo^eȶZYC3U[wMXxRoJ>H$I#N޽vDpu7e͛7yq޲ҫ0ѱ0L0Ɂ^$eUh$rYǞwN?ACT$}N!L{L*3pց6ӼtA"^Džf\_oL?^~M}X vYmBtWmPkgSE :}Fu_>XK?},---+coݻwEt[4y "Bv\.Wg/>խgMm Y>o#'7r,9>K~ͫLV8ӊ+jH~xжm[ccc 8n8KܹSzm(((CӦM#]bUu]!I=O^cW}FG]{ p V0KQ5۲TI:mય_ ȣl<ռ uaESBBڴiC8q"Ç:W9#yy؈zFY+}Ԍ=9Vٙ/x&~5hР(`mmcEGIJ>ˊQasn  ̔ 83 9S"QMVFk q ?JJ s^ʺ :۶߸!iw_ y/u:>:Z֨qfJ9yЕ$ro:N}9L%zh#ߖ|KIIMVo̔T=\޿AFmv|xkoZ]ռ%߶PyO݄G37k  WWkdJ`#晒6 -fFٻ *\p6[||Hv˾}Ҫuf!K=+w ! ;AB@v}tk̔|)) v3 :f%7.}~5 @fWȔb2jxѓ߸e$c."Ci4=O[pڕozД[#VԿB6Ȕ\w+a?~f#'pY[Iw"Oݜ[Я>+y/ˆ@_~;h/EޅV|ܞu+: yJ"у7`]6&,(񓨑eD3%mڊ%붒`.sAP]5sx>epĝ:X28cR2ucZurAg͠"%%bӮ/G6F_*3so^#L;@b-?zZltl Iɩ3fWWykss={4LxkW/ddens͉Aq?I0Sxi&QȔL[{hfzIEoC|+SgЀ/޳NQI9mNDŶaD]C3 nlئ훈ZNk^)6'"**M:]u8涻Xbi q1Llյ:e={tfׁ?*2eʪU)Nx1a2Or_ݹvv )LIzTtHDS ƩkvJ{s~Q;%OmպzG=2iU-E_uq߾}Ȕ`5tsDH%%&V7t3Ov^}C>Ѱ ޲eKö%o%{괆|ld06E>E&<͛XS#ɚ"_K, f[{mYCeEEsUl~pg|<,ԭ$:pI&wk"ܚ5kjI0D)]ฉYh'KwʣdLp#7e㪫1QdJTT]Np&u'ȪE_ "U훘mD奈'qM2v1 !̅o^%C,8t W.Dܼr)]ΧUTɧπA̬kj5R=^Ty=>^-QA|lMgU ʄӻtPFܷ9;jwnaH d7m;:+D.=z[_F*lٲbES,YIsߪueԸ~c-1@/')^ʪij771gA I'¼7vLg-^EryPptɧ%뷹oߐL紿q )'_oU:̘ԟL~UhH&sKLu 5[w [? Dȧ;2חZbnC_Ԫ`󣤄ނڵW[LgԽ=TH6db+ ǗO?*I|1⠺LG\mG1V[ݺI9?obB&BZϱ_훘mD}aӎy~ exx.m$';)s^=﮽Zvw7ۗQ5;w=oǵKEC*oVijxϓ߿T6dx#K[9`pnJj6'.NJ}0Iv˹U.9t9_}&HYU@ û7|H=BuNCi޼<56O4!\H#cW5YHX$S|t-{FLLR.<}iٜ4#ՋQ.ǃ9ƅbEEl_)7-o=^7&+lLHdd?'Spwp 8{3$>f%A|OD%շ|Z]g6{>8!҃~O=OؼJO#5wѓRGhȲY)*=ޭo2=L?HWM, -fggt߾yJ$\ bCՉN_,F߿|=-.?Reg8]BBr֒OjRSGقQE<фp + m;P%*iEya g.&U#_+&6ų3*6Cl&-Wu8;[Gk 9l1C{mz9VwPM Wo^eȶZՙyWo݁+6Zbez?Hm$R%c&߽vyʃ{jhBѕ4<20b+m`D>Z_eh٥g;Gn^QI*G7=A?WtiwΨ]빉j'5 J`p 3x

'
groff -t -man -Tascii -P-c "$1" | \
    perl -e '
        #select STDIN; $| = 1;
        select STDERR; $| = 1;
        #select STDOUT; $| = 1;
        $h = "\010";
        $c0 = undef;
        while(read(STDIN,$c,1) > 0) {

#printf STDERR "c0 = \"%s\"  c = \"%s\"\n",
#	      !defined $c0 ? "" : (ord($c0) < 32 ?
# 				          sprintf("\\%03o",ord($c0)): $c0),
#	      ord($c) < 32 ? sprintf("\\%03o",ord($c)) : $c;

          if (defined $c0) {
            if ($c eq $h) {
              # X ^H * -> bold/italic/something
              read(STDIN,$c1,1);

#printf STDERR " .. c1 = \"%s\"\n",
#	      ord($c1) < 32 ? sprintf("\\%03o",ord($c1)) : $c1;

              if ($c0 eq $c1) {
                # bold
	        if    ($c0 eq "&") { $c0 = "&"; }
		elsif ($c0 eq "<") { $c0 = "<";  }
		elsif ($c0 eq ">") { $c0 = ">";  }
                printf STDOUT "%s",$c0;
              } elsif ($c0 eq "_") {
                # italic
	        if    ($c1 eq "&") { $c1 = "&"; }
		elsif ($c1 eq "<") { $c1 = "<";  }
		elsif ($c1 eq ">") { $c1 = ">";  }
                printf STDOUT "%s",$c1;
              } elsif ($c0.$c1 eq "+o") {
                # Bullet
                printf STDOUT "";
              } else {
                # something -- overstrike ?
	        if    ($c1 eq "&") { $c1 = "&"; }
		elsif ($c1 eq "<") { $c1 = "<";  }
		elsif ($c1 eq ">") { $c1 = ">";  }
                printf STDOUT "%s",$c1;
              }
              $c0 = undef;
              if ($c1 eq "\n") { printf STDOUT "\n"; }
            } else {
              # Not  X ^H *, but X is defined.
              if    ($c0 eq "&") { $c0 = "&"; }
	      elsif ($c0 eq "<") { $c0 = "<";  }
	      elsif ($c0 eq ">") { $c0 = ">";  }
              printf STDOUT "%s",$c0;
              $c0 = $c;
            }
          } else {
            # $c0 not defined!
            $c0 = $c;
          }
        } # ... while()
        if ($c0) { printf STDOUT "%s",$c0; }' |  \
    perl -ne '
        s{(\s*)}{\1}og;
        s{(\s*)}{\1}og;
        s{(\s*)}{\1}og;
        s{_}{_}og;
	# Ordinary man-pages
        s{([-.0-9a-zA-Z_]+)\((\dzm)\)}{\1(\2)}og;

	# Ordinary PERL PODs
        s{([-.0-9a-zA-Z_]+::[-.0-9a-zA-Z_]+)\((\d\w+)\)}{\1(\2)}og;
	print;' | \
    perl -e '
	@labels=();
	while () {
	  if (m{^(.*)$}o) {
	    my $n = $1; $n =~ s/ /_/g;
	    printf "",$n;
	    push @labels, $n;
	  }
	  if (m{^   (.*)$}o) {
	    my $n = $1; $n =~ s/ /_/g;
	    printf "",$n;
	    push @labels, $n;
	  }
	  print;
	}
	printf "

\n

    %s\n",$n,$n; } printf "
\n"; ' echo "
" aprx-2.08.svn593/hlog.c0000644000175000017500000002705512305424775013574 0ustar colincolin/* * aprsc * * (c) Heikki Hannikainen, OH7LZB * * This program is licensed under the BSD license, which can be found * in the file LICENSE. * */ /* * log.c * * logging facility with configurable log levels and * logging destinations */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hlog.h" #include "hmalloc.h" #include "rwlock.h" int log_dest = L_DEFDEST; /* Logging destination */ int log_level = LOG_INFO; /* Logging level */ int log_facility = LOG_DAEMON; /* Logging facility */ char *log_name = NULL; /* Logging name */ char log_basename[] = "aprsc.log"; char *log_dir = NULL; /* Access log directory */ char *log_fname = NULL; /* Access log file name */ int log_file = -1; /* If logging to a file, the file name */ rwlock_t log_file_lock = RWL_INITIALIZER; char accesslog_basename[] = "aprsc.access.log"; char *accesslog_dir = NULL; /* Access log directory */ char *accesslog_fname = NULL; /* Access log file name */ int accesslog_file = -1; /* Access log fd */ rwlock_t accesslog_lock = RWL_INITIALIZER; int log_rotate_size = 0; /* Rotate log when it reaches a given size */ int log_rotate_num = 5; /* How many logs to keep around */ char *log_levelnames[] = { "EMERG", "ALERT", "CRIT", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG", NULL }; char *log_destnames[] = { "none", "stderr", "syslog", "file", NULL }; /* * Quote a string, C-style. dst will be null-terminated, always. */ static int str_quote(char *dst, int dst_len, const char *src, int src_len) { int si; int di = 0; int dst_use_len = dst_len - 2; /* leave space for terminating NUL and escaping an escape */ unsigned char c; for (si = 0; si < src_len; si++) { if (di >= dst_use_len) break; c = (unsigned char) src[si]; /* printable ASCII */ if (c >= 0x20 && c < 0x7f) { /* escape the escape (space reserved already) */ if (c == '\\') dst[di++] = '\\'; dst[di++] = c; continue; } /* hex escape, is going to take more space */ if (di >= dst_use_len - 4) break; dst[di++] = '\\'; dst[di++] = 'x'; di += snprintf(dst + di, 3, "%.2X", c); } dst[di++] = 0; return di; } /* * Append a formatted string to a dynamically allocated string */ char *str_append(char *s, const char *fmt, ...) { va_list args; char buf[LOG_LEN]; int len; char *ret; va_start(args, fmt); vsnprintf(buf, LOG_LEN, fmt, args); va_end(args); buf[LOG_LEN-1] = 0; len = strlen(s); ret = hrealloc(s, len + strlen(buf) + 1); strcpy(ret + len, buf); return ret; } /* * Pick a log level */ int pick_loglevel(char *s, char **names) { int i; for (i = 0; (names[i]); i++) if (!strcasecmp(s, names[i])) return i; return -1; } /* * Open log */ int open_log(char *name, int reopen) { if (!reopen) rwl_wrlock(&log_file_lock); if (log_name) hfree(log_name); if (!(log_name = hstrdup(name))) { fprintf(stderr, "aprsc logger: out of memory!\n"); exit(1); } if (log_dest == L_SYSLOG) openlog(name, LOG_NDELAY|LOG_PID, log_facility); if (log_dest == L_FILE) { if (log_fname) hfree(log_fname); log_fname = hmalloc(strlen(log_dir) + strlen(log_basename) + 2); sprintf(log_fname, "%s/%s", log_dir, log_basename); log_file = open(log_fname, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP); if (log_file < 0) { fprintf(stderr, "aprsc logger: Could not open %s: %s\n", log_fname, strerror(errno)); exit(1); } } rwl_wrunlock(&log_file_lock); if (log_dest == L_FILE) hlog(LOG_DEBUG, "Log file %s %sopened on fd %d", log_fname, (reopen) ? "re" : "", log_file); return 0; } /* * Close log */ int close_log(int reopen) { hlog(LOG_DEBUG, "close_log"); char *s = NULL; if (log_name) s = hstrdup(log_name); rwl_wrlock(&log_file_lock); if (log_name) { hfree(log_name); log_name = NULL; } if (log_dest == L_SYSLOG) { closelog(); } else if (log_dest == L_FILE) { if (log_file >= 0) { if (close(log_file)) fprintf(stderr, "aprsc logger: Could not close log file %s: %s\n", log_fname, strerror(errno)); log_file = -1; } if (log_fname) { hfree(log_fname); log_fname = NULL; } } if (reopen && s) open_log(s, 1); if (!reopen) rwl_wrunlock(&log_file_lock); if (s) hfree(s); return 0; } /* * Rotate the log file */ int rotate_log(void) { char *tmp; int i; char *r1, *r2; if (rwl_trywrlock(&log_file_lock)) { fprintf(stderr, "failed to wrlock log_file_lock for rotation\n"); return 0; } // check if still oversize and not rotated by another thread off_t l = lseek(log_file, 0, SEEK_CUR); if (l < log_rotate_size) { rwl_wrunlock(&log_file_lock); return 0; } // rename tmp = hmalloc(strlen(log_fname) + 6); sprintf(tmp, "%s.tmp", log_fname); if (rename(log_fname, tmp) != 0) { fprintf(stderr, "aprsc logger: Failed to rename %s to %s: %s\n", log_fname, tmp, strerror(errno)); // continue anyway, try to reopen } // reopen if (close(log_file)) fprintf(stderr, "aprsc logger: Could not close log file %s: %s\n", log_fname, strerror(errno)); log_file = open(log_fname, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP); if (log_file < 0) { fprintf(stderr, "aprsc logger: Could not open %s: %s\n", log_fname, strerror(errno)); log_file = -1; } rwl_wrunlock(&log_file_lock); // do the rest of the rotation r1 = hmalloc(strlen(log_fname) + 16); r2 = hmalloc(strlen(log_fname) + 16); for (i = log_rotate_num-1; i > 0; i--) { sprintf(r1, "%s.%d", log_fname, i-1); sprintf(r2, "%s.%d", log_fname, i); if (rename(r1, r2) != 0 && errno != ENOENT) { fprintf(stderr, "rename %s => %s failed:%s\n", r1, r2, strerror(errno)); } } if (rename(tmp, r1) != 0) { fprintf(stderr, "aprsc logger: Failed to rename %s to %s: %s\n", tmp, r1, strerror(errno)); } hfree(tmp); hfree(r1); hfree(r2); return 0; } static int hlog_write(int priority, const char *s) { struct tm lt; struct timeval tv; char wb[LOG_LEN]; int len, w; // Wall clock time gettimeofday(&tv, NULL); gmtime_r(&tv.tv_sec, <); if (log_dest & L_STDERR) { rwl_rdlock(&log_file_lock); fprintf(stderr, "%4d/%02d/%02d %02d:%02d:%02d.%06d %s[%d:%lx] %s: %s\n", lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday, lt.tm_hour, lt.tm_min, lt.tm_sec, (int)tv.tv_usec, (log_name) ? log_name : "aprsc", (int)getpid(), (unsigned long int)pthread_self(), log_levelnames[priority], s); rwl_rdunlock(&log_file_lock); } if ((log_dest & L_FILE) && (log_file >= 0)) { len = snprintf(wb, LOG_LEN, "%4d/%02d/%02d %02d:%02d:%02d.%06d %s[%d:%lx] %s: %s\n", lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday, lt.tm_hour, lt.tm_min, lt.tm_sec, (int)tv.tv_usec, (log_name) ? log_name : "aprsc", (int)getpid(), (unsigned long int)pthread_self(), log_levelnames[priority], s); wb[LOG_LEN-1] = 0; rwl_rdlock(&log_file_lock); if ((w = write(log_file, wb, len)) != len) fprintf(stderr, "aprsc logger: Could not write to %s (fd %d): %s\n", log_fname, log_file, strerror(errno)); rwl_rdunlock(&log_file_lock); if (log_rotate_size) { off_t l = lseek(log_file, 0, SEEK_CUR); if (l >= log_rotate_size) { rotate_log(); } } } if (log_dest & L_SYSLOG) { rwl_rdlock(&log_file_lock); syslog(priority, "%s: %s", log_levelnames[priority], s); rwl_rdunlock(&log_file_lock); } return 1; } /* * Log a message with a packet (will be quoted) */ int hlog(int priority, const char *fmt, ...) { va_list args; char s[LOG_LEN]; if (priority > 7) priority = 7; else if (priority < 0) priority = 0; if (priority > log_level) return 0; va_start(args, fmt); vsnprintf(s, LOG_LEN, fmt, args); va_end(args); return hlog_write(priority, s); } /* * Log a message, with a packet in the end. * Packet will be quoted. */ int hlog_packet(int priority, const char *packet, int packetlen, const char *fmt, ...) { va_list args; char s[LOG_LEN]; int l; if (priority > 7) priority = 7; else if (priority < 0) priority = 0; if (priority > log_level) return 0; va_start(args, fmt); l = vsnprintf(s, LOG_LEN, fmt, args); va_end(args); str_quote(s + l, LOG_LEN - l, packet, packetlen); return hlog_write(priority, s); } /* * Open access log */ int accesslog_open(char *logd, int reopen) { if (!reopen) rwl_wrlock(&accesslog_lock); if (accesslog_fname) hfree(accesslog_fname); if (accesslog_dir) hfree(accesslog_dir); accesslog_dir = hstrdup(logd); accesslog_fname = hmalloc(strlen(accesslog_dir) + strlen(accesslog_basename) + 2); sprintf(accesslog_fname, "%s/%s", accesslog_dir, accesslog_basename); accesslog_file = open(accesslog_fname, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP); if (accesslog_file < 0) hlog(LOG_CRIT, "Could not open %s: %s", accesslog_fname, strerror(errno)); rwl_wrunlock(&accesslog_lock); return accesslog_file; } /* * Close access log */ int accesslog_close(char *reopenpath) { hlog(LOG_DEBUG, "Closing access log..."); rwl_wrlock(&accesslog_lock); hlog(LOG_DEBUG, "Closing access log, got lock"); if (close(accesslog_file)) hlog(LOG_CRIT, "Could not close %s: %s", accesslog_fname, strerror(errno)); hfree(accesslog_fname); hfree(accesslog_dir); accesslog_fname = accesslog_dir = NULL; accesslog_file = -1; if (reopenpath) { return accesslog_open(reopenpath, 1); } else { rwl_wrunlock(&accesslog_lock); return 0; } } /* * Log an access log message */ int accesslog(const char *fmt, ...) { va_list args; char s[LOG_LEN], wb[LOG_LEN]; time_t t; struct tm lt; int len; ssize_t w; va_start(args, fmt); vsnprintf(s, LOG_LEN, fmt, args); va_end(args); s[LOG_LEN-1] = 0; time(&t); gmtime_r(&t, <); len = snprintf(wb, LOG_LEN, "[%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s\n", lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday, lt.tm_hour, lt.tm_min, lt.tm_sec, s); wb[LOG_LEN-1] = 0; rwl_rdlock(&accesslog_lock); if (accesslog_file >= 0) { if ((w = write(accesslog_file, wb, len)) != len) hlog(LOG_CRIT, "Could not write to %s (fd %d): %s", accesslog_fname, accesslog_file, strerror(errno)); } else { if (accesslog_file != -666) { hlog(LOG_ERR, "Access log not open, log lines are lost!"); accesslog_file = -666; } } rwl_rdunlock(&accesslog_lock); return 1; } /* * Write my PID to file, after locking the pid file. * Leaves the file descriptor open so that the lock will be held * as long as the process is running. */ int pidfile_fd = -1; int writepid(char *name) { int f; char s[32]; int l; f = open(name, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (f < 0) { hlog(LOG_CRIT, "Could not open %s for writing: %s", name, strerror(errno)); return 0; } pidfile_fd = f; if (flock(f, LOCK_EX|LOCK_NB) < 0) { if (errno == EWOULDBLOCK) { hlog(LOG_CRIT, "Could not lock pid file file %s, another process has a lock on it. Another process running - bailing out.", name); } else { hlog(LOG_CRIT, "Failed to lock pid file %s: %s", name, strerror(errno)); } return 0; } l = snprintf(s, 32, "%ld\n", (long)getpid()); if (ftruncate(f, 0) < 0) { hlog(LOG_CRIT, "Could not truncate pid file %s: %s", name, strerror(errno)); return 0; } if (write(f, s, l) != l) { hlog(LOG_CRIT, "Could not write pid to %s: %s", name, strerror(errno)); return 0; } return 1; } int closepid(void) { if (pidfile_fd >= 0) { if (close(pidfile_fd) != 0) { hlog(LOG_CRIT, "Could not close pid file: %s", strerror(errno)); return -1; } pidfile_fd = -1; } return 0; } aprx-2.08.svn593/hlog.h0000644000175000017500000000270012273250104013552 0ustar colincolin/* * aprsc * * (c) Heikki Hannikainen, OH7LZB * * This program is licensed under the BSD license, which can be found * in the file LICENSE. * */ #ifndef LOG_H #define LOG_H #define LOG_LEN 2048 #define L_STDERR 1 /* Log to stderror */ #define L_SYSLOG (1 << 1) /* Log to syslog */ #define L_FILE (1 << 2) /* Log to a file */ #ifdef __CYGWIN__ #define L_DEFDEST L_FILE #else #define L_DEFDEST L_STDERR #endif #define LOG_LEVELS "emerg alert crit err warning notice info debug" #define LOG_DESTS "syslog stderr file" #include extern char *log_levelnames[]; extern char *log_destnames[]; extern int log_dest; /* Logging destination */ extern int log_level; /* Logging level */ extern char *log_dir; /* Log directory */ extern int log_rotate_size; /* Rotate log when it reaches a given size */ extern int log_rotate_num; /* How many logs to keep around */ extern char *str_append(char *s, const char *fmt, ...); extern int pick_loglevel(char *s, char **names); extern int open_log(char *name, int reopen); extern int close_log(int reopen); extern int hlog(int priority, const char *fmt, ...); extern int hlog_packet(int priority, const char *packet, int packetlen, const char *fmt, ...); extern int accesslog_open(char *logd, int reopen); extern int accesslog_close(char *reopenpath); extern int accesslog(const char *fmt, ...); extern int writepid(char *name); extern int closepid(void); #endif aprx-2.08.svn593/ChangeLog0000644000175000017500000024540212320105344014230 0ustar colincolin2014-03-24 Matti Aarnio - OH2MQK - KP20NG * interface.c: Igated 3rd-party frames are produced with different TNC2 format message from AX.25 frame message. This is because TNC2 format is used at filter processing, and needs to have original source address arriving from APRS-IS. * ttyreader.c: Set default read timeout to 60 minutes, it can be configured to any value from 1 second to 4 hours as channel busyness supports, and even disabled. * ttyreader.c: Use aprxlog() to record tty state changes: close/open * netresolver.c: Use same "die_now" flag as all other thread loops do. * historydb.c: Fix key pickup of sourcename. Debug print key in history_db_insert_() * agwpesocket.c: Code cleaning. * cellmalloc.c: No need to include * configure.in: Make pthread autoconfig default with option to disable it. * pbuf.c: Declare pbuf_alloc() static. 2014-03-22 Matti Aarnio - OH2MQK - KP20NG * aprx.c: Fix aprxlog() function internal varargs usage. It must be reset for each vfprintf() call... * aprx.c: Initial value of time_reset = 1; start in "reset state", which does time resetting in all prepoll codes in main loop. At second round and there after of the main loop the reset_time flag will be reset after execution of all prepoll functions. * aprsis.c: Do initial connect at about current tick + 10 seconds. Reset phase will also put next connect attempt at that time. * aprx.c: Change stdout and stderr buffering definition to have a buffer, and make line buffering in a way that works.. * aprsis.c: Correct calls to aprxlog(). * configure.in, Makefile.in: Revised the pthread support autoconfig to really find where the libraries are, and set correct definitions and compile/link time parameters. 2014-03-21 Matti Aarnio - OH2MQK - KP20NG * aprx.h, interface.c, beacon.c, config.c, digipeater.c, telemetry.c: Change the "flags are bits in an int" to "packet bit fields" allowing direct setting of flags. * aprx.c, beacon.c: Do not use stdout FILE* to print things out of signal handler. Use sprintf(3) to put things into local buffer, then use write(2) to send them to file handle 1. Otherwise GLIBC has some interlock issue causing a deadlock in rare case. (Happened with debug printouts in development.) * beacon.c: Correct close() of exec type beacon file descriptor. * interface.c: Correct primary flags defaults, and setting of "telem-to-is " and "telem-t-rf " flags. * telemetry.c: Debug-printout (debug>1) of telemetry about time until next output, plus packet that is being telemetered along with hex coding of interface flags. Also streamlined the telemetry label output code. 2014-03-11 Matti Aarnio - OH2MQK - KP20NG * ttyreader.c: Do open /dev/ttySnnn up front in non-blocking mode so that if the serial port hardware needs to be wired specially to be considered active ( = flow control ) the serial port open will not stop -- hang -- at it missing. * agwpesocket.c: Update time comparison codes to current model. * aprx.c: time_reset gets cleared only after first round through the main loop. * beacon.c: The 'file' and 'exec' directives need at most 256 bytes of read buffer to get beacon body content. No need for 2kB buffer alloc. 2014-03-08 Matti Aarnio - OH2MQK - KP20NG * digipeater.c: Corrected the digipeating recognition algorithm. If the current leading VIA field value without H-bit set is not recognized as transmitter callsign, transmitter alias, or value of or directive at digipeater source or digipeater transmitter configuration, then the packet is _not_ eligible for digipeat. 2014-03-08 Matti Aarnio - OH2MQK - KP20NG * aprx.h, beacon.c, digipeater.c, parse_aprs.c, telemetry.c, timercmp.c, ttyreader.c: Fix all things flagged at CFLAGS="-g -O2 -Wall". * configure.in, config.h.in, aprx.c: Autoconf test for , and include if available. * aprx.h, beacon.c, config.c, digipeater.c, interface.c, netax25.c, telemetry.c, doc/aprx-manual.odt, aprx.8.in: Change single boolean flag 'txok' into multiple interface flags where the 'txok' is just one bit. Added 'telem-to-is ' option to * aprsis.c: Warn at startup if an block does not contain passcode. 2014-03-04 Matti Aarnio - OH2MQK - KP20NG * aprx.c, aprx.h, beacon.c: Pickup child process exit SIGCHLD signals, and track which child did exit. * beacon.c, aprx.c, aprx.h, aprx.conf.in, aprx.8.in: Kamil Palkowiski SQ8KFH's beacon exec idea rewritten to be non-blocking in execution. The Aprx main loop does not tolerate blocking programs. * aprsis.c, ttyreader.c: poll(2) results on file handles asking for POLLIN can include POLLHUP and POLLERR. 2014-03-04 Matti Aarnio - OH2MQK - KP20NG * aprx.8.in, doc/aprx-manual.odt: Document passcode a bit more. * aprsis.c, aprx.c, aprx.h, netax25.c: Move aprxlog() from aprsis.c to aprx.c. Use aprxlog() instead of open coded logger at netax25.c. Modify which type of APRSIS events get logged without the runtime -L option to the aprxlogfile. 2014-02-26 Matti Aarnio - OH2MQK - KP20NG * Makefile.in, agwpesocket.c, aprsis.c, aprx-stat.c, aprx.c, aprx.h, aprxpolls.c, beacon.c, config.h.in, configure.in, digipeater.c, dprsgw.c, dupecheck.c, erlang.c, filter.c, historydb.c, hlog.c, igate.c, interface.c, kiss.c, netax25.c, netresolver.c, pbuf.c, telemetry.c, timercmp.c, ttyreader.c: Change primary time management to use system monotonic clock instead of time-of-day clock that can jump back/forward. This clock is in -lrt as: clock_gettime(CLOCK_MONOTONIC, ...) and it is the kernel internal primary jiffy lock. This value is not synchronized with wall clock, although it does proceeds at about same rate. It is also present at FreeBSD, and presumably several other platforms too. (It is a POSIX thing, after all.) The system also watches over if the delta in between subsequent value extracts exceeds about 30 seconds, or the time jumps backwards at all. If such happens, next call cycle on all prepoll routines are resetting all time management things in particular subsystem. Renamed the primary time tracking variable from "now" to "tick", as it has no correlation with current wall-clock time. There are no Y2038 issues in this code approach. 2014-02-24 Matti Aarnio - OH2MQK - KP20NG * agwpesocket.c, aprsis.c, digipeater.c, dptsgw.c, dupecheck.c, filter.c, interface.c, netresolver.c: Y2038 comparison things of time values. Somewhat premature, but gets things in line with uses of 'struct timeval' -- tv_timercmp(). 2014-02-23 Matti Aarnio - OH2MQK - KP20NG * all files: Change copyright statement to read 2007-2014. * timercmp.c, Makefile.in: New file bringing all struct timeval tools to one place. Added tv_timerbounds() that monitors time targets vs. current time value -- if machine has hibernated a lot without running timers and the 'now' jumps ahead a lot (or backwards for complete coverage), then the target time is reset. This avoids running a burst of beacon events when 'now' jumps ahead a lot, as an example. * netax25.c, digipeater.c, beacon.c, ttyreader.c, telemetry.c, dprsgw.c, agwpesocket.c, aprsis.c, aprx.h, historydb.c, dupecheck.c, erlang.c: Timer tracking against possibly jumping 'now' with conditional resetting. * beacon.c, dupecheck.c, netax25.c, telemetry.c, timercmp.c: Few wrong initializations of timers fixed. 2014-02-13 Forgot to record the author * debian/control: Add build and run dependency of OpenSSL. (Actually not yet necessary, SSL support is not being compilable / compiled yet.) 2014-02-13 Heikki Hannikainen * parse_aprs.c: Check that the timestamp ends with one of valid timestamp type ids. Those being: 'z', 'h', '/' 2014-02-03 Matti Aarnio - OH2MQK - KP20NG * ttyreader.c: Read only when poll reports data availability. (uh3ack) 2014-02-01 Matti Aarnio - OH2MQK - KP20NG * rpm/aprx.spec.in: Forcing RPM building to produce i386 package. (There is no need to produce x86-64 binary package. Small footprint is the goal.) * erlang.c: Do not call aprx_syslog_init() from this module. * ssl.c, ssl.h, hlog.c, hlog.h: Copied SSL client code material from Aprsc. Not yet functional. * agwpesocket.c, beacon.c, digipeater.c, dupecheck.c, erlang.c, telemetry.c, ttyreader.c: If the time reported by time(2) seems to jump ahead or backwards too much, various deadline schedulers keeping time goals in mind will reset themselves upon detection of the condition. 2013-11-01 Matti Aarnio - OH2MQK - KP20NG * historydb.c: Initialize also tokenbucket value so that freshly arrived packet at the historydb will have at least 1.0 tokens at hand to permit one initial digipeat. Will not be enough for multi-transmitter case. * digipeater.c: Display Ratelimit caused packet drops at all debug levels. ( Instead of >1 ) * filter.c: Correct parse of s// filter. (Original author not recorded.) 2013-10-08 Matti Aarnio - OH2MQK - KP20NG * config.c, digipeater.c: Add conditionals so that --disable-igate works. 2013-10-07 Matti Aarnio - OH2MQK - KP20NG * Makefile.in, svnversion-test.sh: A bit more complicated way to pick up SVNVERSION information. * aprx.c, VERSION, README, TODO, INSTALL, doc/aprx-manual.odt Version 2.08 under way. * aprx.h, aprx.c, aprsis.c, aprx-stat.c, aprxpolls.c, beacon.c, digipeater.c, dupecheck.c, erlang.c, interface.c, telemetry.c, ttyreader.c: Changed time tracking from "time_t" to "struct timeval". This supports (sub)millisecond granularity e.g. in KISS polling, and overall smoothness of processing. No, it isn't really necessary besides of that KISS polling, but unified API is always better. 2013-10-06 Matti Aarnio - OH2MQK - KP20NG * filter.c: Add 'g/call1/call2..' filter to match "group messaging" recently added on javAPRSSrvr. * doc/aprx-manual.odt: Document internally supported APRS-IS style filter tokens. * filter.c: Code cleaning around reference definitions. * filter.c: * Add feature to recognize 3rd-party frames by type (t/3) * Enable support of D filter (they are meaningful at RF too) * filter.c, aprx.c, aprx.h, config.c: * Fix M filter preparation to include cos(lat). * aprx.h, interface.c, parse_aprs.c: Always look inside 3rd party APRS frames too to determine their content for filtering uses. RF->RF relaying didn't do that. 2013-10-05 Matti Aarnio - OH2MQK - KP20NG * configure.in, Makefile.in, aprx.c, aprx.8.in: Support integrated binary version printout: aprx -V * configure.in, Makefile.in: Preparing for new code features, picking up more library locations. * aprsis.c: Improve the error diagnostics on upstream socket connection error situations. * beacon.c: Debug report number of parsed beacons. 2013-10-03 Geoffrey F4FXL * digipeater.c: Fixed badly formatted AX.25 after insertion of MYCALL value on VIA path. The end-pointer was mis-managed. 2013-08-10 Matti Aarnio - OH2MQK - KP20NG * configure.in: Look for OpenSSL library, SCTP network protocol. 2013-08-03 Heikki Hannikainen * aprsis.c, aprs-complex.conf.in, aprx.8.in, aprx.conf.in, doc/aprx-manual.odt: Require manually configured APRS-IS passcode. 2013-08-03 Matti Aarnio - OH2MQK - KP20NG * agwpesocket.c, aprsis.c, beacon.c, cellmalloc.c, dprsgw.c, dupecheck.c, filter.c, historydb.c, keyhash.c, pbuf.c, telemetry.c, ttyreader.c: Coding style change: Use cmalloc() instead of malloc() + memset(). 2013-08-03 Frank Knobbe * interface.c: Correct the way how messages are passed from APRS-IS to RF. There was a parse bug around pb->dstname field. 2013-04-25 Matti Aarnio - OH2MQK - KP20NG * netax25.c: Receive from AX.25 interface only if it has associated an Aprx interface configuration object with it. Less noise on logs that way. 2013-04-25 Matti Aarnio - OH2MQK - KP20NG * filter.c: Commented out code, but make it conform with API of historydb. * parse_aprs.c: The call parameter 'historydb' can be NULL, don't SEGV on it. * telemetry.c: Pointer writing to beyond end of buffer (one last time) * ttyreader.c: hexdumpfp() text dump fix * beacon.c: Restructured the beacon data preparation for transmit. Lots of mistakes had crept in recently (2.06?) * interface.c, netax25.c: Debug printouts of processing errors, and transmissions. 2013-04-23 Matti Aarnio - OH2MQK - KP20NG * config.c: Show 'myloc' lat/lon in degrees instead of radians. * digipeater.c: debugging.. * filter.c: A fence-post error while parsing o/A8CDEF-12 -- accepted only A8CDEF-1. 2013-04-22 Matti Aarnio - OH2MQK - KP20NG * aprx.8.in, aprx-complex.conf.in, aprx.conf.in, doc/aprx-manual.odt, doc/aprx-manual.pdf: Change documentation wording around . * config.c: Fix myloc config parameter parsing. * beacon.c: Every group is now independent from each other. If you define multiple groups, they will be running in parallel with their own scheduling. Meaning that you can have different cycles, and defaults at each. * all source files: Change Copyright claim years to 2007-2013. * configure.in: Auto-test for header file. 2013-04-22 Geoffrey F4FXL * digipeater.c: Recent rewrite of viafield tracking code forgot to initialize it in many cases -- and Covarity tool did not notice that :-( 2013-04-20 Matti Aarnio - OH2MQK - KP20NG * aprsis.c: Preparing for new communication modes * parse_aprs.c: Two condition expression goofups that were always "false" * filter.c: Match filter parsing with aprsc (issue found with Covarity testing) * aprsis.c, aprx.c, aprx.h, aprxpolls.c, cellmalloc.c, config.c, digipeater.c, dprsgw.c, filter.c, interface.c, kiss.c, netax25.c, telemetry.c, ttyreader.c: Covarity testing revealed unclear error cases, unnecessary comparisons against NULL, and other mostly harmless things. There is still a memory leak in interface.c configuration parsing in error path -- but that is not a big deal. * digipeater.c: Modified "fixall" processing to put transmitter callsign into first VIA field in every case, possibly truncating the VIA list in order to make room for the callsign in the incoming request. Replacing numeric literals with #define constants to inline document of what is going on at places. 2013-04-18 Matti Aarnio - OH2MQK - KP20NG * aprx.c, VERSION: Version 2.07 (APRX27) * aprx.8.in: Document "myloc" and "$myloc", "filter m/100", etc. * aprx.c, aprx.h: Variables for myloc_lat, myloc_lon, and string forms. * config.c: Parse "myloc lat xx long yy" entry. Moved couple tool functions here from beacon.c. * filter.c: Support "m/100", if top-level configuration has "myloc lat xx lon yy" entry. * beacon.c: new "macro" definition of "$myloc", which takes lat+lon of top-level configuration "myloc.." entry. * interface.c: Recognizing incoming messages targeted to this server (by $mycall, and transmit interface callsigns.) Processing them by rudimentary acknowledgement, and optional place to actually process them. Actual processing is still not written. 2013-02-02 Matti Aarnio - OH2MQK - KP20NG * ttyreader.c: Improve the error report, when serial port opening failed. * filter.c: Coverity reports over aprsc reflect directly to aprx too. We use same code in parts... 2012-12-29 Matti Aarnio - OH2MQK - KP20NG * digipeater.c, aprx.h, historydb.h: Track digipeated messages by source callsign. * aprsis.c: Use A->rdlin_len instead of strlen(A->rdline) * aprx-stat.c, aprx.c, aprx.h, erlang.c: Moved syslog init from erlang to aprx core. * parse_aprs.c, aprx.h: Function parse_aprs_message() for planned future use. 2012-12-09 Matt Maguire VK2RQ * telemetry.c: Sometimes the Erlang registery contains interface entries that are no longer actually in the system. Avoid NULL referral at such situations. 2012-11-09 Matti Aarnio - OH2MQK - KP20NG * dprsgw.c: Better accounting on DPRS side-channel data occupancy, assuming the DPRS gw does send bytes all the time. 2012-11-09 FUJIURA Toyonori JG2RZF * dprsgw.c: Add receiver Erlang estimators to received packets. 2012-11-01 Matti Aarnio - OH2MQK - KP20NG * aprsis.c: Move CRLF appending later into APRSIS transmission. 2012-10-31 Matti Aarnio - OH2MQK - KP20NG * aprx.c: Use thread-safe gmtime_r() instead of gmtime() for time printout. 2012-10-29 Matti Aarnio - OH2MQK - KP20NG * Makefile.in: Improve distribution tar RPM building support. * rpm/aprx.init, rpm/aprx.service, rpm/aprx.spec.in: RPM package packing files * filter.c: Clean filter parser. * aprx.c: Acquire a lock on pid-file, and keep the file open. 2012-10-29 FUJIURA Toyonori JG2RZF * dprsgw.c: D-STAR callsign formatting for suffixless ones: * "JG2RZF A" -> "JG2RZF-A" * "JG2RZF " -> "JG2RZF-" Oops! 2012-10-12 Matti Aarnio - OH2MQK - KP20NG * rpm/aprx.spec.in, rpm/aprx.service: Andrew Elwell supplied better version of Fedora, and rhel. * filter.c: Leave the "over-long parameters are rejected" logic, but don't support alternate group field splitter input. 2012-10-03 Matti Aarnio - OH2MQK - KP20NG * TODO, INSTALL, aprx.c, aprx-manual.odt, aprx-manual.pdf, VERSION: 2.06 2012-10-02 Matti Aarnio - OH2MQK - KP20NG * filter.c: Parse filters with callsign sets without overflowing buffer. Officially syntax is: OP/callsign1/callsign2 but support also: OP/callsign1,callsign2 * digipeater.c: Correct analysis of first "via" field, bail out of the loop only after it has been parsed. 2012-10-01 Matti Aarnio - OH2MQK - KP20NG * debian/postinst, debian/aprx.init: Removed postinst from debian installations. That script is bad and causes install/upgrade to stall. Modernized the aprx init-script. 2012-09-30 Matti Aarnio - OH2MQK - KP20NG * aprx.h, digipeater.c, interface.c, pbuf.h, pbuf.c, parse_aprs.c: Sync pbuf_new() implementation with aprsc's approach, and sync parse_aprs() with aprsc's code. This should fix some strange MICe bugs, among others. 2012-09-18 Matti Aarnio - OH2MQK - KP20NG * parse_aprs.c: Corrected MICe longitude degrees parser. Again. (Sorry, F4FXL's parser fix was wrong.) 2012-09-05 Matti Aarnio - OH2MQK - KP20NG * apparmor.aprx, debian/rules, debian/dirs, debian/postint: Copied bits of Aprsc's debian packaging to Aprx. (Original packaging stuff was copied from Aprx to Aprsc.) 2012-09-02 Matti Aarnio - OH2MQK - KP20NG * aprx.8.in, aprx.conf.in, aprx-complex.conf.in, aprx-rxigate.conf.in, configure.in: Exterminated all instances of yours truly's own address from the sample configurations, and replaced them with 0000.00N 00000.00E coordinate, which aprs.fi treats as invalid. * beacon.c: In addition to the long time used qTYPE_LOCALGEN, added on APRSIS beacons also ",TCPIP*" at the tail of whatever is being sent. Pete is not happy with q-code alone... 2012-08-26 Matti Aarnio - OH2MQK - KP20NG * digipeater.c, interface.c, aprx.h, TODO, aprx-manual.*: Add to APRSIS tx-gated packets optionally different via-path (parameter: msg-path) for message packets, than for any other type packets. For example: via-path WIDE1-1, msg-path WIDE2-2 (By request of OH3BK.) * VERSION: 2.05 * beacon.c, interface.c: Support beaconin to APRSIS without having any radio interfaces. Make null-device interface and (in special conditions the internal APRSIS interface) beaconable. * doc/aprx-manual.*: Fixes on explanations. Diagrams on subsystem configuration examples describing message flows. 2012-08-07 Matti Aarnio - OH2MQK - KP20NG * configure.in, aprsis.c, ROADMAP, doc/aprx-manual.odt: Fix the "pthreads" typo by using correct "pthread" spelling in the documents + "--with-pthreads" option alias in configure. 2012-08-07 Matti Aarnio - OH2MQK - KP20NG * interface.c: Automatically create igate-group N values for radio interfaces start from 1, and go onwards. Manually defined values start from one, and it is up to the configuration writers to have a sane upper limit. * aprx.c, aprx.h, ttyreader.c, erlang.c, kiss.c: Fixed a) poll of KISS subinterfaces, b) KISS polling time management issues. The polling does not yet have serial-device specific timing management, rather a global cadence. * aprx.c: Version 2.05 * ttyreader.c, aprxpolls.c, aprx.c, aprx.h: Do millisecond timings, send KISS POLL (0x0E) code requests every configured number of milliseconds per serial-device: serial-device ... KISS pollmillis 100 * agwpesocket.c, aprsis.c, aprx.c, aprx.h, aprxpolls.c, aprx-stat.c, beacon.c, configure.in, digipeater.c, dprsgw.c, dupecheck.c, erlang.c, filter.c, historydb.c, igate.c, interface.c, kiss.c, netax25.c, netresolver.c, pbuf.c, telemetry.c, ttyreader.c: Changed "now" from type "time_t" to "struct timeval" enabling the ttyreader to do millisecond level timing operations. 2012-08-06 Matti Aarnio - OH2MQK - KP20NG * debian/aprx.init, rpm/aprx.init: Correct the package start dependency definitions. 2012-08-04 Matti Aarnio - OH2MQK - KP20NG * interface.c, kiss.c: Debug printout claimed reversed logical meaning in history-db lookup. Typo fixes at kiss processing comments. * interface.c: Config parser bug in defining multiple . Thanks to N2PYI for report. 2012-07-31 Matti Aarnio - OH2MQK - KP20NG * configure.in, digipeater.c, aprx.h, valgrind.c: Supply implementation of memrchr() if the platform does not have it. (GNU Libc extension, not a standard POSIX thing.) * Makefile.in: Fixing details around how to run "make make-deb". Fixing how SVNVERSION is determined. * interface.c, igate.c, aprx.h: rflog() parameter set modification, show directly at calls if the logged item is 'R' or 'T'. * dupecheck.c, aprsis.c, igate.c, aprx.h, digipeater.c: Prepare for the duplicate checker to be able to control the time window of the duplicate checks - minimum will be 30 seconds, maximum can be higher. * digipeater.c: TNC2 format quirks in "VIA field" data caused missed identification of "ping pong relay" - where a packet contains this node's transmitter callsign, and this node has logged its address as "sent through me". Also known as TRACE mode: SRC>DEST,MYTRANS,OTHERTX*,WIDE3-1 2012-07-30 Matti Aarnio - OH2MQK - KP20NG * keyhash.c, keyhash.h, netax25.c, netresolver.c, interface.c, cellmalloc.c, config.c, dprsgw.c, ax25.c: A compiler test with -Wall complained a bit around missing ANSI-C prototypes. 2012-07-29 Matti Aarnio - OH2MQK - KP20NG * keyhash.c, filter.c: Threw away the long unused CRC32 stuff, and added a hashkeyuc() function. Fixed the way how filter_entrycall_*() and filter_wx_*() functions processed the key. Now both of them store keys as converted to upper case, and lookup with mixed case. * aprx-config.xsd: An attempt at writing a "formal" configuration file syntax description. It is really not XML that config file, nor can XSD define it clearly, I think.. 2012-07-28 Matti Aarnio - OH2MQK - KP20NG * VERSION: 2.04 * filter.c: Wrong way to put len* fields into an union vs. a few other things. * dupecheck.c, digipeater.c: Fix from iw3ijq + k3pdk about AX25 duplicate tracking dropping null pointers. This has _not_ been validated for TNC mode frames yet, but now AX.25 works properly. 2012-01-18 Matti Aarnio - OH2MQK - KP20NG * VERSION: 2.03test4 * interface.c: Record outbound "history heard" per interface. * historydb.c: Simple refactoring to use same hash function all the time. * digipeater.c: Feed all transmitted packets to dupe-filter of that transmitter. This will handle cross-band digipeat + tx-gates so that an APRS packet transmitted to a channel won't be digipeated again by this transmitter. * igate.c: When constructing APRSIS originated 3rd-party tx-igate packets, use correct version of "TCPIP" in the header. 2012-01-07 Matti Aarnio - OH2MQK - KP20NG * VERSION: 2.03test3 * pbuf.h, pbuf.c, aprx.h: New infrastructure function: pbuf_fill() that does former two (then 3) copies of same code in interface.c * interface.c: Use new pbuf_fill(). On 3rd-party receiver, parse original (sort of) TNC2 frame to temporary AX.25 frame for filter analysis. Rearranged filter running and reactioning. * filter.c: Debug printouts * All files... Copyright text update. 2011-12-30 Matti Aarnio - OH2MQK - KP20NG * interface.c: 3rd-party igate tx processing fixes, now it allows also other than messages 2011-12-25 Matti Aarnio - OH2MQK - KP20NG * VERSION, aprx.c, TODO, INSTALL, doc/aprx-manual.*: Version 2.03 * interface.c: At top-level of the the "callsign" parameter did not override device defined callsign and its AX.25 parse result. * aprx.h, config.c, dprsgw.c, logrotate.aprx.in, aprx.conf.in, aprx-complex.conf.in: Configurability of logging parameter "dprslog". * aprx.h, aprsis.c, beacon.c, igate.c, telemetry.c: Supply qcode on outgoing packets depending it being locally generated (qAS) or rx-i-gated (qAR) * telemetry.c: Fix telemetry "tocall" to use software identifier, and to put "TCPIP*" on APRSIS beacon path. * aprx.conf.in: Fix default server list * crc.c, kiss.c: Document polynomes and fix comments 2011-08-29 Matti Aarnio - OH2MQK - KP20NG * parse_aprs.c: F4FXL found bug in position parser cos() pre-calculator. Input needs to be in radians, it was in degrees. 2011-07-26 Matti Aarnio - OH2MQK - KP20NG * interface.c: If preceding address processing detects an error, do not call packet transmitter. * aprx.h, dprsgw.c, parse_aprs.c: Moved DPRS related APRS symbol translation from parse_aprs.c to dprsgw.c. * parse_aprs.c: Introduced APRS symbol mapper/generator for GPS messages. * parse_aprs.c: Rewrite of parse_aprs_mice() degrees parser inspired by F4FXL. 2011-03-16 Matti Aarnio - OH2MQK - KP20NG * beacon.c, telemetry.c, aprx.h, interface.c: Transmit only radio data port related beacons, and telemetry. Don't touch on other pseudo-interfaces. 2011-01-02 Matti Aarnio - OH2MQK - KP20NG * interface.c, agwpesocket.c, aprx.h: Configure agwpe-device. * netresolv.c: Oops.. fixed functionality. Tuned debugging. * aprsis.c, aprx.c: Tuned debugging. * configure.in, aprsis.c: Autoconfig stdarg.h existence, use varargs on debug printout code. * agwpesocket.c: Debug printout on received frame. * interface.c, doc/aprx-manual.odt: Unlimit the number of "igate-group N" parameter's N value range. * aprsis.c, aprx.c, beacon.c, config.c, digipeater.c, dprsgw.c, filter.c, historydb.c, igate.c, interface.c, keyhash.c, kiss.c, netresolver.c, ttyreader.c: Cleaning compiling on a very old FreeBSD -- essentially K&R C compiler (gcc 2.95.4) not permitting variable declarations anywhere but beginning of code block. 2011-01-01 Matti Aarnio - OH2MQK - KP20NG * interface.c: Always init interface specific Erlang accounting * filter.c: Negative ranges on filters means "outside the distance". This allows adding "substractive" filters that tell "don't pass messages to receipients outside given range". * configure.in, interface.c, cellmalloc.c, timestamp.c, kiss.c, historydb.h, netax25.c, pbuf.h, aprx.c, aprx.h, filter.c: Clean autoconfig tests, make the code compilable on oldish FreeBSD. * configure.in, aprx.h, aprx.c, agwpesocket.c, interface.c: Configuration option --enable-agwpe to enable AGWPE socket interface code. * configure.in, aprx.h, aprx.c, aprsis.c, ax25.c, beacon.c, digipeater.c, dpwsgw.c, erlang.c, filter.c, historydb.c, igate.c, interface.c, parse_aprs.c, telemetry.c, ttyreader.c: Configuration option --disable-igate for embedding code without igate network connection. Disables DPRS GW code, and all of historydb, igate.c, aprsis.c. * aprx.c, VERSION, INSTALL, README, TODO Version: 2.02 * aprx.h, interface.c: A null-device for sinking infinite digipeat output. A debug tool. * aprx.h, interface.c, historydb.c, historydb.h, pbuf.h: Properly track "heard from interface(group)" information for tx-igate processing. Now a full APRSIS traffic feed won't transmit anything to RF, unless explicit manual filter definitions tells to transmit, or message recipient has been recently heard behind a radio channel X. See the new 'igate-group' parameter on documents. * aprsis.c, netresolver.c: Cleaned pthreads thread creation calls. * agwpesocket.c: Erlang account on outbound socket. * digipeater.c: Allow ratelimit parameter to be defined very high. This way a full APRSIS traffic feed can be sent to null-device. * aprx.conf.in, aprx-complex.conf.in: Some new notes on digipeater and tx-igate controls. * doc/aprx-manual.odt: Document update. 2010-12-18 Matti Aarnio - OH2MQK - KP20NG * interface.c, aprx.h: Add a "null-device" for system. Never receive anything from it, have infinite transmit capacity. * netresolver.c, aprx.h, aprx.c: A pthread that resolves dynamic DNS names on background, and updates static copy of address data so that main loop can safely do asynchronous connections without needing to do synchronous DNS resolving processing. * aprsis.c: Use pthread_cancel() in smart way to expedite shutdown. * agwpesocket.c, aprx.h, aprx.c: Draft of a socket communication mechanism with AGWPE, and LDSPED. 2010-12-17 Matti Aarnio - OH2MQK - KP20NG * VERSION, INSTALL, README, TODO Version: 2.01 * doc/aprx-manual.odt: Write a few pages about debugging * aprx.c: Version tocall code: APRX21 * aprx.c, aprx.8.in: Add '-i' option to keep the program foreground without enabling any debug printouts. * digipeater.c: Correct the recognition of "probably heard on first hop" * historydb.c: Memory realloc() bug fixed. * kiss.c: Recognize a write attempt on closed file handle, don't write. 2010-08-08 Matti Aarnio - OH2MQK - KP20NG * VERSION: 2.00 * aprx.c: Version tocall code: APRX20 * doc/aprx-manual.odt: version 2.00/1.00 2010-08-02 Matti Aarnio - OH2MQK - KP20NG * aprx.c: Version tocall code: APRX1M * ROADMAP: Planning update. * beacon.c, aprx.8.in, aprx.conf.in, aprx-complex.conf.in: OH7MMT complained difficulty of defining beacons on Rx-iGate. The default aprx.conf shows the use of "interface ..", which requires tx-enabled target interfaces. Something that rx-only will not have. * doc/aprx-manual.odt: Cross references, additional notes on digipeat of non-APRS packets. 2010-07-27 Matti Aarnio - OH2MQK - KP20NG * config.c: Accept valid APRSIS login callsigns for "mycall" parameter value. Will complain latter if end usage did want valid AX.25 callsigns instead. 2010-06-25 Matti Aarnio - OH2MQK - KP20NG * interface.c: Account the number of created interfaces and subinterfaces, and if there is just one and no callsign has been defined, only then supply a default value ("callsign $mycall"). * interface.c: Check interface callsigns to be unique. * aprsis.c, beacon.c, digipeater.c, interface.c, telemetry.c: Recognize "$mycall" in case insensitive matching. 2010-06-22 Matti Aarnio - OH2MQK - KP20NG * aprx.c: Version tocall code: APRX1L * digipeater.c: If there is a viscous-delay defined, add a random value of 0..2 to the configured number of seconds delay. This way similarly configured adjacent Tx-iGates can at random find a first transmitter and others will drop the Tx-iGate. * digipeater.c: Digipeater didn't detect interface callsigns and aliases properly, when determining if there is some work to be done. It did detect them when to avoid doing duplicate work. 2010-06-19 Matti Aarnio - OH2MQK - KP20NG * aprx.c, aprx.h, aprsis.c, beacon.c, config.c, digipeater.c, interface.c, telemetry.c, ttyreader.c: Improve config file parsing error reporting. Any ERROR observed on configuration parsing causes immediate abort with exit-code 1. * aprx.conf.in, aprx-complex.conf.in, aprx.8.in: Clarify the documentation at man-page, and at config templates. * firmware/* Copied one source of TNC2 firmwares * windows/* Just a placeholder. * Makefile.in: Improving "make dist" behaviour. 2010-06-13 Matti Aarnio - OH2MQK - KP20NG * aprx.8.in, doc/aprx-manual.odt: Some notes on available baud-rates, fixes on documentation about CRC algorithms. * ttyreader.c: Missed baud speed 57600. * dupecheck.c: Shrink the arena allocation size to 4 kB. This allows 14 reference packets in the dupecheck dataset within that arena alone, and will possibly request second arena when this get really busy. * historydb.c, dupecheck.c, pbuf.c, filter.c: Made cell object sizes readable with a debugger * crc.c: Object visibility adjustments. * Makefile.in, crc.c, aprx.h, kiss.c, ttyreader.c, interface.c: Separated KISS processing to kiss.c, and CRC processing to crc.c. Disabled unused codes. * pbuf.c, historydb.c, dupecheck.c: Shrink RAM usage, cellmalloc() does perfectly good job of using free-chains, no need to do it in applications. * interface.c, pbuf.c: The pbuf_new() has a limit on maximum total size that a packet can be: 2150 bytes. If ax25len + tnc2len are more than that, pbuf_new() returns NULL. * historydb.c, historydb.h: Instead of historydb instance specific history entry pools, have one global pool. 2010-06-12 Matti Aarnio - OH2MQK - KP20NG * cellmalloc.c: Remove pthread_mutex_t usage, not needed in this application! Conditionalize debugging code. * pbuf.c, aprx.c, aprx.h: Using cellmalloc() on pbuf storage. Support up to 2150 byte received AX.25 frames. (Up to 7 pbufs of that size on 16 kB cell arena .. and there never should be larger than around 512 byte AX.25 frames.) * dupecheck.c: Shrink dupecheck to 16 kB per alloc arena (from 256 KB). That should be enough for a very busy 1200 bps channel, and probably even for a 9600 bps channel. * filter.c: Shrink filter cell storage to 4 kB per alloc arena (from 512 kB!) * historydb.c: Shrink historydb cell storage to 32 kB per alloc arena (from 128 kB) * debian/aprx.init: Pleasing init-script behaviour during package re-install * telemetry.c, doc/aprx-manual.odt: Stagger telemetry transmissions a bit. All RF reported telemetry sources are reported at the same time, but telemetry data is time-wise separated from labels, and labels are sent so that 3 different labels are sent every 2 hours, and total label carouselle time is 6 hours. * aprx.c: Giving this version code: APRX1K * telemetry.c: Correct AX.25 data frame for RF transmission * telemetry.c, config.c, aprx.h, doc/aprx-manual.odt, aprx.conf.in, aprx-complex.conf.in: New config section, be able to tx telemetry to RF ports. * beacon.c: Error reporting additions. * erlang.c, aprx-stat.c, aprx.h, telemetry.c: Use pre-processor conditionals to select only small subset of storage and processing codes used for erlang data in embedded mode. * parse_aprs.c: Compile warning silencing. 2010-06-10 Matti Aarnio - OH2MQK - KP20NG * kiss.c, ttyreader.c, dprsgw.c, aprx.h: Tracked specification origins of each of the three different CRC-16 calculations that this code has. What was called "crc_ccitt" was in fact something totally different: the SMACK CRC16. 2010-06-07 Matti Aarnio - OH2MQK - KP20NG * README, ROADMAP, TODO, INSTALL, PROTOCOLS: Updates on plans and rough guides. * TIMESTAMP-AT-APRSIS, timestamp.c: A proposal to put about 1ms resolution timestamps on APRSIS with full backwards compatibility. * aprx.c: Giving this version code: APRX1J * dprsgw.c, digipeater.c, aprx.h, doc/aprx-manual.odt: Clean DPRS Rx gateway code. * interface.c: Correct 3rd-party packet gating filter behaviour: If there are _no_ filters, pass all packets thru. * interface.c, aprx.h, dprsgw.c, igate.c: Rate-limit source callsigns per source interface to once per 30 seconds. RF iGate DPRS messages to APRS as 3rd-party messages. 2010-06-06 Matti Aarnio - OH2MQK - KP20NG * parse_aprs.c, dprsgw.c: Rudimentary GPSxyz -> APRS symbol mapping for DPRS, with GPSxyz overlay character where possible. Also IDENT TEXT, if any. * debian/aprx.init: The "restart" command fails to work quite often. The reason being "set -e"... * aprx.h, igate.c, dprsgw.c, ttyreader.c, ax25.c: igate_to_aprsis(... , STRICTAX25) -- new flag to control source specific information about TNC2 format address formats. * parse_aprs.c: Remove unnecessary double precission floating point math. Use of 'float' is quite enough. * debian/control: Change package name a bit * dprsgw.c: Data collection code fixes * telemetry.c: Send first telemetry at 20.0 minutes after start. This is related to defaulting to EMBEDDED operation mode. * configure.in, aprx.h, erlang.c: Change defaults so that System is always "--with-embedded", and requires explicit "--with-erlangstorage" to compile with a filesystem based backing storage codes. 2010-06-04 Matti Aarnio - OH2MQK - KP20NG * aprx.h, dprsgw.c: Couple steps onwards with DPRS Rx-IGate. 2010-06-01 Matti Aarnio - OH2MQK - KP20NG * aprx.c: Set software tocall to 'APRX1H'. * interface.c: Found a memory leak on AX.25 type packet receiver. Oops! Ax.25 packets that were rejected by 's filters were just leaked, not disposed. * historydb.c: Allocate cell arenas in 128 kB blocks. * digipeater.c: Valgrind complained about uninitialized variables. Default zero is not good enough... 2010-05-30 Matti Aarnio - OH2MQK - KP20NG * aprx.h, digipeater.c: Use 'float' for ratelimit variables. Permitting as low rate definitions as 'once per 10 minutes. * dprsgw.c: XOR checksums of "GPS" mode packets. No Rx-iGate of them yet. 2010-05-29 Matti Aarnio - OH2MQK - KP20NG * dprsgw.c, ttyreader.c, aprx.c, aprx.h: Experimental DPRS receiver. Studying packet identification from serial port datastream with Rx-IGate of GPS-A type DPRS packets. * interface.c, doc/aprx-manual.odt: Effects of 'filter ...' on different sections are subtly different. See the manual! * aprx.h, digipeater.c, doc/aprx-manual.odt, aprx.conf.in, aprx-complex.conf.in: Remodelled the ratelimiting to use 'token bucket filter': ratelimit average burst both values are packets per minute, and both default to 60. * digipeater.c, interface.c: Return adjunk filters to RF->RF digipeating. They got lost a bit back with Tx-IGate needing them placed a bit differently. * man-to-html.sh: groff has changed its -Tascii output a bit, and its own HTML output is horrible... 2010-05-27 Matti Aarnio - OH2MQK - KP20NG * parse_aprs.c: Correct parse of message recipients. Fix from Patrick Domack K3PDK. 2010-05-26 Matti Aarnio - OH2MQK - KP20NG * debian/rules, rpm/aprx.spec.in, aprsis.c: Fixes on pthread:ed aprsis implementation. Default packaging for Debian and RPM to use --with-pthread. Now it shows only one "process", and two threads. This makes the program portable to uCLinux, and possibly to Windows. * dprsgw.c, aprx.h, ttyreader.c, Makefile.in, interface.c, aprx.8.in, aprx.conf.in, aprx-complex.conf.in: Add basic configuration and infra of DPRS-to-APRS gw. * interface.c, igate.c: Comment editing, adding Tx-IGate Rule 4, second half. First half probably exists in igate.. 2010-05-25 Matti Aarnio - OH2MQK - KP20NG * aprx.c, README, TODO, VERSION: Mark version 1.99, APRX1G 2010-05-25 Matti Aarnio - OH2MQK - KP20NG * interface.c, filter.c: Filtering logic change on 3rd-party digipeating. 2010-05-24 Matti Aarnio - OH2MQK - KP20NG * interface.c, TODO: Notes about missing bits for full Tx-IGate. * historydb.c, historydb.h: Keep knowledge of last coordinate on historydb. Mark also last coordinate update time. * aprx.h, pbuf.h, parse_aprs.c, interface.c, digipeater.c: Tx-IGate processing details.. 2010-05-23 Matti Aarnio - OH2MQK - KP20NG * aprx.c, aprx.h, digipeater.c, filter.c, historydb.c, historydb.h, igate.c, interface.c, parse_aprs.c: Rearranged bits so that Tx-IGate's processing has transmitter specific history database. Better (but yet a bit incomplete) Tx-IGate filtering is in interface.c. * kiss.c, aprx.8.in, aprx.conf.in, aprx-complex.conf.in, netax25.c, aprx.h, ttyreader.c: Add FLEXNET support. (Finnish HamDR speaks it by default!) * interface.c: Corrections on definitions, in particular the aliases. Fixed Tx-IGate workhorse interface_receive_3rdparty()'s packet address header construction. * digipeater.c, aprx.h: APRSIS specific via-path parameter parsing fixes, additional debugging outputs, config error detection outputs. * netax25.c, ax25.c: Debugging outputs. * ttyreader.c, aprx.h: Debugging outputs. Special KISS subtype "RFCRC" - received KISS frames have two RANDOM bytes at their end. Looks like CRC16, but is random junk. * erlang.c: Debug printout. * beacon.c: When an interface has no callsign, do not send beacon to it. (It could be tty master interface which has multiple KISS sub-interfaces.) * igate.c: Parse packet to be tx-igated for acceptable address syntaxes. Debug printouts. 2010-05-16 Matti Aarnio - OH2MQK - KP20NG * aprsis.c, aprx.conf.in, aprx.8.in, doc/aprx-manual.odt: Emphasize that 's login parameter is to be set ONLY when it needs to be different than mycall value. (But tolerate also '$mycall' alias.) * aprx.c: Update internal tocall default to 'APRX1F'. * interface.c, digipeater.c: Tx-iGate fixes for case where outgoing packet has no VIA field(s). System saw that "requested hops = 0, done hops = 0 -> no need to send out". * parse_aprs.c: Make sure that all things creating debug output are followed by a "\n". * igate.c: Relax station callsign format rules inside a received 3rd-party frame. 2010-05-13 Matti Aarnio - OH2MQK - KP20NG * interface.c: Under all situations, fill in pbuf_t->dstcall_end. * debian/rules: Do not strip resulting objects. We want debug symbols! * digipeater.c: Improve config error reporting. * config.c: Tolerate config file ending without a newline. 2009-12-27 Matti Aarnio - OH2MQK - KP20NG * aprx.c, VERSION: 1.98 * rpm/aprx.spec, debian/docs, debian/control: More documents into the packages * beacon.c, aprx.8.in, aprx.conf.in, aprx-complex.conf.in, doc/aprx-manual.odt, doc/aprx-manual.pdf: Changes on beacon definition keywords to make them clearer for a user. Old forms continue as silently accepted aliases. 2009-12-27 Matti Aarnio - OH2MQK - KP20NG * VERSION: 1.97 * beacon.c, aprx.8.in, aprx.conf.in, aprx-complex.conf.in: Never send to APRSIS interface. Define 'object' and 'item' type named entities. Uppercasify several of input fields. * interface.c, digipeater.c, aprx.h: On tx-igate behaviour, add configured via-path on outgoing 3rd-party frames 2009-12-16 Matti Aarnio - OH2MQK - KP20NG * beacon.c: Reworked more.. beacons were sent only for "tx-ok true" interfaces, while netbeacons are to be sent for all interfaces. 2009-12-03 Matti Aarnio - OH2MQK - KP20NG * beacon.c: Reworked things a bit, on how and with what content to send beacons. * netax25.c: Valgrind pleasing * telemetry.c, ttyreader.c, beacon.c, netax25.c, aprx.h: Debug printouts. 2009-12-01 Matti Aarnio - OH2MQK - KP20NG * interface.c: Register tty KISS subinterface 0 only once. * beacon.c: When a file-beacon is defined, the message content is no longer at bm->msg, instead at local variable msg. * netax25.c, aprsis.c: Pleasing valgrind. 2009-11-26 Matti Aarnio - OH2MQK - KP20NG * parse_aprs.c: Sometimes ran memchr() with bad length. Caused rare crash. * digipeater.c, aprxpolls.c, netax25.c, aprsis.c, aprx.c, aprx.h: Valgrind tested minor changes. 2009-11-17 Matti Aarnio - OH2MQK - KP20NG * aprx.8.in: Big rewrite. * aprx.h, interface.c, digipeater.c, netax25.c: Digipeater "relay-type directonly" mode. Interface parsing error detection improvement. 2009-11-08 Matti Aarnio - OH2MQK - KP20NG * VERSION 1.96 * beacon.c, aprx.8.in, aprx.conf.in, aprx-complex.conf.in: Rewrote beacon transmission interval codes, and documents of the beacon system. * configure.in, Makefile.in: Added --with-pthread option, rewrote the --with-embedded option processing. * aprsis.c, configure.in: Optionally support pthreads(3) where available, this can be important for uCLinux and perhaps Windows, where fork(2) is not available. 2009-11-07 Matti Aarnio - OH2MQK - KP20NG * aprx.c: Software tocall identity: APRX1C * cellmalloc.c: Drop semaphores. Not used in this codebase. * filter.c, aprx.h, parse_aprs.c, Makefile.in: Port Aprsc's filter code to Aprx. * debian/aprx.init, rpm/aprx.init: Update Debian's version, sync the rpm's version * telemetry.c: Report every 20 minutes, but scale values telling 10 minute traffic. Start sending telemetry PARM blocks from system start. * aprx.c: Init configurations before starting erlang accounting subsystem. 2009-11-02 Matti Aarnio - OH2MQK - KP20NG * VERSION 1.95 * aprx.c: Software tocall identity: APRX1B * telemetry.c: Erlang reporting confusion fixed. 2009-10-30 Matti Aarnio - OH2MQK - KP20NG * interface.c: More logging of interface setups. * beacon.c, aprx.8.in, aprx.conf.in, aprx-complex.conf.in New type of beacon: file /path/to/file, the first line in given file is read every time this beacon entry is executed, and the text is sent out sans line end LF. There are no quote processing available. . Validate latitude and longitude input, and complain if validate failed. . Beacon timefixing on all message types that support it, and only upon special "timefix" option set on the beacon. . Beacon construction from small parts supports 6 different fundamental packet types with coordinates. * config.c: Make config parser debugging needs -ddd runtime options. * telemetry.c: Report higher of 10 minute integrated packet/byte counts in the 20 minute account interval. This makes graphs smoother. 2009-10-27 Matti Aarnio - OH2MQK - KP20NG * aprx.h, aprx.c, aprsis.c, beacon.c, netax25.c, ttyreader.c, erlang.c, aprx-stat.c, igate.c: Modify the printtime() to print millisecond resolution times. 2009-10-26 Matti Aarnio - OH2MQK - KP20NG * beacon.c, config.c, aprx.h, aprx.conf.in, aprx-complex.conf.in, aprx.8.in: Added a 'beaconmode { aprsis | both | radio }' config option, which says where the beacons are sent to. * ROADMAP Development roadmap for first digit of version number 2009-10-25 Matti Aarnio - OH2MQK - KP20NG * digipeater.c, beacon.c, interface.c, aprx.h: Put all beacons on digipeater's transmitter duplicate checker storage. Now a beacon with WIDE2-2 path will not be repeated by message originator. Re-organized beacon's code base. * netax25.c, interface.c, aprx.h: Use Link-Level mechanism to send arbitrary AX.25 packets to desired devices. Added also a about once minute run code that checks current AX.25 devices in the system, and maps necessary ifindex:es to netdevices wanting to do IO. And all parameters to 'netax25_sendto()' are const.. * aprx.h, aprx.c, aprx-stat.c, aprsis.c, erlang.c, igate.c: Commonly used strftime() got put into printtime() function. * VERSION 1.94 * interface.c: That final part of thing (below) can only be done, if this interface has non-null tty field. * Makefile.in "make valgrind" - helps debugging some screwups.. * digipeater.c: Stop scanning the viscous queue, once time limit exceeds current time. * aprxpolls.c: A screwup on allocations found with valgrind. Oops! * netax25.c, aprsis.c, dupecheck.c, ttyreader.c, erlang.c, aprx.c, aprx.h, ax25.c, beacon.c, igate.c, historydb.c, valgrind.c, Makefile.in: Code changes to clean valgrind outputs on nuisance messages. And at least one bugfix for valgrind environment... 2009-10-24 Matti Aarnio - OH2MQK - KP20NG * telemetry.c: Changed telemetry channel 2: Erlangs from bytes_tx accounting. Now there is separate channel load graph showing how much this transmitter is affecting the channel. Give also better label for channel 4: IGateDropRx * rfbeacon.c -> beacon.c, aprx.h, aprx.c, config.c, Makefile.in: Rename rfbeacon.c to beacon.c. * interface.c: As a final part of definition, check if the default tnc-subid (0) has associated ttycallsign defined, and netax25 pty not enabled. If so, create it. * parse_aprs.c, interface.c, aprx.h: Depending upon usage, either do not look inside 3rd-party frame, or do look inside. For Tx-iGate call paths we need the analysis! * parse_aprs.c, interface.c, historydb.c, digipeater.c, rfbeacon.c, dupecheck.c, igate.c: Removed codes ignoring "trailing CRLF in APRS frame tail". They kept causing more trouble than good. The parse_aprs.c is anyway diverged from its origins a bit more than I would like, so no need to maintain mess related on its original environment. * dupecheck.c, aprx.h: Adjust dupecheck hash bucket size - 16 is quite sufficient enough even for a very busy igate/digi! * igate.c, aprx.h, ax25.c, ttyreader.c Restructured 3rd-party frame rx-igate processing so that it does not need to alter TNC2 format buffer in any way. This caused some weird things to happen in digipeater on cases of 3rd-party frames. Also modified ax25-to-tnc2 UI-formatter so that there will always be zero termination of output text string. * interface.c, digipeater.c: Small restructuring while debugging other things. * parse_aprs.c: Parse APRS 3rd-party frame content, and recognize real 3rd-party frame for sure. 2009-10-24 Matti Aarnio - OH2MQK - KP20NG * VERSION: 1.93 * ttyreader.c, erlang.c: Feeding in generated random noise did raise a few errors in KISS frame processing, and erlang accounting. KISS tncid:s sometimes did not have a callsign associated with them, and then erlang accounting blew up... * interface.c, rfbeacon.c, ax25.c, aprx.h: Beacons to radio interfaces, KISS works best, NET-AX.25 less well -- transmits OK UI frames, transmit of arbitrary other types of frames is necessary for generic digipeating. * digipeater.c: Add alias to config parameter 'transmit' -> 'transmitter'. * netax25.c, config.c: Removed unused parameter on netax25_addrxport(). Rebuilt netax25_sendto(), however it sends out only UI frames.. Removed pre-created tx_socket, and associated codes. * configure.in: Remove unused autoconf test for cfmakeraw().. 2009-10-23 Matti Aarnio - OH2MQK - KP20NG * rfbeacon.c: Fix the debug printout of beacon to be sent. * aprx.h, config.c: readconfigline() Keep the cf->linenum showing first source line number on continued multiline. This helps a bit on debugging, and similar things are already done when a (sub)group parser is reporting where that subgroup begins in case of missing parameters in a group. * digipeater.c: Validating received requests better. Now marking also probable situations that can say "this came from originator to me directly" * netbeacon.c, rfbeacon.c, config.c, aprx.h, aprx.c, aprsis.c, Makefile.in: Removing the old netbeacon code. 2009-10-22 Matti Aarnio - OH2MQK - KP20NG * aprx.h, rfbeacon.c, interface.c: Implemented new infra for sending APRS beacons to radio interfaces. Incoming request will at first construct the AX.25 header, then send that to physical interfaces. * aprx.h, ax25.c, digipeater.c, interface.c, kiss.c, pbuf.h, pbuf.c, netax25.c, parse_aprs.c, rfbeacon.c, ttyreader.c: Changed all instances of 'unsigned char' to 'uint8_t'. 2009-10-21 Matti Aarnio - OH2MQK - KP20NG * config.c: Clean away some superflous debugs. * digipeater.c: Viscous mode debug printout crashes on SEGV.. Oops. (without debug the bug does not hit.) * README.ViscousDigipeater, digipeater.c: Thinking about what are correct rules on Viscous Digipeat. * digipeater.c: On configuration, make sure that no two definitions within single definition have same :s. 2009-10-20 Matti Aarnio - OH2MQK - KP20NG * aprx.h, digipeater.c: Viscous digipeater special corner cases.. * aprx.h, config.c, aprx.conf.in, aprx-complex.conf.in Interval parser for "timeout" and "interval" parameters used in several places. * config.c, aprx.conf.in, aprx-complex.conf.in: Line continuation (the '\' character at the end of input line - with possible whitespaces following it) is now supported. An interval parser is added; timeouts and intervals can be defined in human readable way: 3m2s * aprx.8.in: Larger rewrite to bring it up to current version. * aprsis.c, aprx.conf.in, aprx-complex.conf.in: Reworked details of configuring, and internal operational semantics. Most notable difference is on possibility to define used filters in small fragments, which the system will then catenate. * aprx.h, digipeater.c, dupecheck.c: Returned the viscous delay processing back to version just before "simplified viscous delay and dupechecking". It really needs to check delayed vs. immediate counts. 2009-10-19 Matti Aarnio - OH2MQK - KP20NG * VERSION: 1.92 * digipeater.c, interface.c: Clean variable naming on digipeater subsystem. * aprx.h, aprx.conf.in, aprx-complex.conf.in, igate.c: Update documentation * igate.c: Reject packets with destcall=RXTLM-* from Tx-IGate. * digipeater.c: Simplify control flow when feeding to viscous queue vs. when running the backend directly. * aprx.h, digipeater.c, dupecheck.c, igate.c: - Fixed 3rd party dupechecking - Simplified viscous delay and dupechecking. The dupechecking happens at the start of digipeater_backend(), which is called after pbuf's have been subjected to a viscous delay, if ever necessary. First arriving packet is digipeated, if it has steps to do. If not, rest are still considered dupes, and not digipeated. * aprx.h, igate.c: Make rflog() public function to be used by multiple parties. Also mark on the log line the direction to which the packet was going (R/T), and not only its source interface. * ax25.c: Moved igate_to_aprsis() call before interface_receive_ax25(), and thus also log received packet on rf.log before it is sent out anywhere. * aprx.h, interface.c, digipeater.c, aprx.8.in, aprx.conf.in, aprx-complex.conf.in, dupecheck.c: Moved viscous-delay processor into digipeater's control area. Implemented Tx-IGate's packet re-formatting rules on interface.c. Implemented viscous-delay processing in digipeater. Added refcounting on dupe_record_t objects. * digipeater.c, igate.c, config.c, aprx.h, aprx-complex.conf.in: Moved regexp reject filters from old style setup at igate.c to new style at digipeater.c. This will not be able to reject trivially configurable things from Rx-IGate datastreams, but it can be used to control digipeating. * ax25.c: Remove unnecessary debug printout. 2009-10-18 Matti Aarnio - OH2MQK - KP20NG * VERSION: 1.91 * aprx.h, dupecheck.c, digipeater.c, pbuf.c, pbuf.h, aprsis.c, igate.c, keyhash.c: Digipeater local instance of dupechecker. The dupe-checker does recursive analysis of APRS packets for 3rd party frames, and dupecheck the innermost frame of them. Cleaned the keyhash.c to contain only FVN-1a hasher. * digipeater.c, interface.c, telemetry.c, aprx.h, ttyreader.c, erlang.c, aprx-stat.c: Start transmitting digipeated frames to ttyreader's KISS output. netax25's similar interface is not tested. Changed also the Erlang dataset format, and begun to produce Tx packet counts on telemetry. Accounting saves also tx byte counts, but they are not reported. * aprx.h, interface.c, digipeater.c, pbuf.h, pbuf.c, ax25.c: Pass the UI PID information all the way to interface, where it can be matched against a list of PIDs that will be treated alike APRS in digipeating. Have two modes in digipeating: one with interface (and aliases) as recognized work targets, and other with APRS wide/trace tags in addition to interface (and aliases). * aprx.h, ax25.c, interface.c, digipeater.c, aprx.conf.in, aprx-complex.conf.in: - Prefill AX.25 address field formatted ax25callsign field on all datastructures. That comes handy when doing TRACE processing. - Put exact "callsigns" of "WIDE", "TRACE", and "RELAY" into system as interface callsign aliases. When they are present in the request path, substitute interface callsign there with H-bit set. - Update aprx.conf samples to how the system can be configured. 2009-10-17 Matti Aarnio - OH2MQK - KP20NG * aprx.h, digipeater.c, interface.c, pbuf.h, parse_aprs.c: Digipeater preparation, system counts done and requested distribution operations with source specific as well as transmitter specific keywords. * aprx.h, ax25.c, interface.c, aprsis.c, pbuf.c, parse_aprs.c: Changed a few "parse failed" returns to "parse OK". Systematic feed of datapacket "with TNC2 line end CRLF pair". 2009-10-16 Matti Aarnio - OH2MQK - KP20NG * erlang.c, aprx.h, ttyreader.c, netax25.c, igate.c: Clean Erlang accountig API. Removed unused parameters. * interface.c, aprsis.c, igate.c, aprx.h, aprx.c, digipeater.c: Minimal definition parsers. Additionally internal "APRSIS" pseudo-interface. 2009-10-15 Matti Aarnio - OH2MQK - KP20NG * aprx.h, aprx.c, rfbeacon.c: Add internal "tocall" constant with value "APRX19". That one will track software versions. The 'for' keyword on beacons defaults to $mycall. * aprx.h, aprx.c, interface.c, config.c, rfbeacon.c, aprsis.c, ttyreader.c, netbeacon.c: Config machinery redone towards new style. Rx-IGate works again. (Old config should work too.) * aprx.h, pbuf.c, pbuf.h, interface.c, digipeater.c, aprx.c: Skeletons for pbufs, digipeaters, and their uses in the interface layer. * aprsdigi.c, ax25.c, Makefile.in: Remove obsolete placeholder. Things will be done differently. * config.c, rfbeacon.c: The rfbeacon code will be doing all variants of beacons.. * interface.c, netax25.c, ttyreader.c, aprx.h, ax25.c: interface_transmit_ax25() is able to transmit a fully formed AX.25 header+control+body frame to Linux internal AX.25 network devices as well as to any serial port attached KISS device. Transmission to TNC2 devices is not supported. Code is also very careful on checking the AX.25 frame header structure, and rejecting outright any with invalid header structure (bad continuation flags, bad chars in callsigns, not so careful with SSID byte contents - too many are careless with those :-( ) 2009-10-14 Matti Aarnio - OH2MQK - KP20NG * rfbeacon.c, interface.c, Makefile.in, config.c, aprx.c, aprx.h, aprx.conf.in, aprsis.c, netbeacon.c: Add a stub of rfbeacon. 2009-10-13 Matti Aarnio - OH2MQK - KP20NG * interface.c, config.c, netax25.c, ttyreader.c, ax25.c, aprx.h, ChangeLog: Feed received AX.25 frames to interface layer for possible digipeat processing. (Missing: APRSIS originated frames!) * interface.c, ttyreader.c, aprx.h, aprx.conf.in, aprx.8.in: Remodelled serial-device definitions into interface layer. Documentation updates to match new system. 2009-10-13 Matti Aarnio - OH2MQK - KP20NG * aprsis.c, aprx.h, config.c, netbeacon.c, aprx.conf.in, VERSION, README, INSTALL, TODO: interface config with new style entry. More of definitions. Experiments at aprx.conf.in writing. * Makefile.in, aprx.8.in, aprx.conf.in, aprx.h, config.c, filter.c, interface.c, netax25.c, netbeacon.c, pbuf.c, pbuf.h, ttyreader.c: Incremental work on new style of configurations as outlined in the Requirement Specification document. Old style configurations do still work. Serial port initstring is now binary transparent. 2009-10-05 Matti Aarnio - OH2MQK - KP20NG * VERSION: 1.06 * netbeacon.c: Complete beacon coordinate validator code, now it can detect invalid input values properly. * netax25.c, aprsis.c, ttyreader.c, igate.c, aprx.h: Cleaning gcc -Wall warnings on various platforms, including OpenBSD. 2009-10-01 Matti Aarnio - OH2MQK - KP20NG * VERSION: 1.05 * kiss.c, netax25.c, ttyreader.c, aprx.h, ax25.c: Write the SMACK frame with correctly escaped CRC. Fixed also serial-port initstring handling. 2009-09-30 Matti Aarnio - OH2MQK - KP20NG * VERSION: 1.04 * netax25.c: When writing AX.25 KISS frame to kernel, try to do it up to 3 times. Also add some debug statements on ax25-rxport processing. * VERSION: 1.03 * ttyreader.c: Fix on serial port startup - always turn on flows on the port, and explicitely flush the driver level buffers discarding possibly accumulated data. 2009-09-28 Matti Aarnio - OH2MQK - KP20NG * VERSION: 1.02 * erlang.c, aprx-stat.c, aprx.h: Remove subport from erlang codes, having "tncid N" on serial port definitions takes care of this kind of things. * kiss.c, ttyreader.c, netax25.c, ax25.h: Moved KISS/SMACK encoder to separate module, the CRC16 calculator went there as well. For each ttyreader sub-tncid there is separately opened KISS-pty channel on Linux systems with given callsign as interface's writer channel. The netax25 ax25-port reader does not accept packets with source callsign as any of our ttyreader callsigns. * erlang.c: Fix the erlang_find() to really find the interface call. 2009-09-07 Matti Aarnio - OH2MQK - KP20NG * cellmalloc.c, netax25.c, keyhash.c: Compiling at OS/X found a few odd problems, corrected. 2009-08-30 Matti Aarnio - OH2MQK - KP20NG * aprx.8.in, aprx.conf.in, config.c, aprx.h, aprx.c, telemetry.c, netax25.c, netbeacon.c, aprsis.c, ttyreader.c, erlang.c, aprx-stat.c: Rename the "mycall" configuration parameter to "aprsis-login", what it really is being used at. There is no "mycall", anywhere! * dupecheck.c, aprx.h, aprx.c, [dupecheck.h]: Removed dupecheck.h after incorporating it into aprx.h. Added the poll-interfaces to handle housekeeping operations. * beacon.c -> netbeacon.c, Makefile.in, aprsis.c, aprx.c, aprx.h: Change file name, and all references therein. A preparation for separate RF beacons. * netbeacon.c, aprx.8.in, aprx.conf.in: Add "netbeacon dest APRS via NOGATE ..." options for configuration. * netbeacon.c: Use float math to determine next event times for all beacons for smoother distribution. 2009-08-29 Matti Aarnio - OH2MQK - KP20NG * Makefile.in, dupecheck.c, dupecheck.h, aprx.c, aprx.h, aprsis.c: Add infrastructure for future: dupecheck() * telemetry.c: Add "NOGATE" on telemetry messages transmitted to APRSIS. * igate.c: Deeper look into Rx-IGate specs revealed couple missing details. More bits towards Tx-IGate. 2009-08-23 Matti Aarnio - OH2MQK - KP20NG * VERSION, INSTALL: Mark version as: 1.00 * aprx.c: Document '-L' option. * aprx.h, aprsis.c, beacon.c, igate.c: New internal API to pass data from aprx proper, and APRS-IS communicator. This is able to carry binary (including NUL bytes) data both on received AX.25 address, and frame content. * telemetry.c: Change a bit on information texts, and transmit frequency. * aprx.conf.in, aprx.8.in: Edit prototype configurations, and documentation * aprx.c, aprx.h, igate.c, cellmalloc.h, historydb.h, historydb.c, keyhash.h, keyhash.c, pbuf.h, parse_aprs.c: Preparing infrastructure for TX capable i-gate, and digipeater. 2009-02-10 Matti Aarnio - OH2MQK - KP20NG * Makefile.in, aprsdigi.c, igate.c, aprx.h, ax25.c, ttyreader.c, netax25.c, aprsis.c: Move rx-igate code to igate.c,and make initial moves to collect information about what to do for tx-igate. * PROTOCOLS, TODO, README: Document updates. 2008-12-07 Matti Aarnio - OH2MQK - KP20NG * aprx.c, aprsis.c, netax25.c, configure.in, ttyreader.c, Makefile.in: Compile testing to get this to work on Solaris 10. Also fixes on PIPE failure handling (correct SIGPIPE ignoring) on platforms other than Linux -- and possibly also for Linux. Now this should drop in to FreeBSD and OS/X as well. 2008-10-28 Matti Aarnio - OH2MQK - KP20NG * netax25.c, aprx.8.in: Turned upside-down the meaning of ax25-rxport config parameter. There is no longer a wild-card receiving mode in Linux internal AX.25 network receiving. All APRS receiving interface callsigns must be listed explicitely. * netax25.c, ttyreader.c, aprx.h: SMACK probe transmits on link that is configured for it. Also offers some debug messages on SMACK activation. * beacon.c, ax25.c: Cleaning debug printouts 2008-07-18 Matti Aarnio - OH2MQK - KP20NG * config.c: Function for validate of callsign input syntax * telemetry.c: Please the compiler a bit, increment the telemetry sequence number only after all telemetered interfaces have been reported. * aprsis.c: If there is need to reconnect to APRSIS, pick all possible IP addresses for it, and use them all. Also improved the use of new IP resolver API a bit. * ttyreader.c: Explicitely set "KISSSTATE_SYNCHUNT = 0" in enumeration. Memory blocks are created with memset() call clearing them. * aprx.h, beacon.c: Compiler pleasing * erlang.c: One-off array size handling, resulted in SEGV... 2008-04-11 Matti Aarnio - OH2MQK - KP20NG * VERSION: 0.22: * erlang.c: Auto-embed the erlang-dataset if backing-store open fails. * ttyreader.c: Mark closed socket as closed. 2008-03-29 Matti Aarnio - OH2MQK - KP20NG * VERSION: 0.21 * telemetry.c: New "send Erlang data as telemetry packets to APRS-IS" subsystem. * aprsis.c: Fix the APRS-IS network login protocol. There are TWO parameter strings after the "vers" keyword. * configure.in, Makefile.in, erlang.c: Support compilation as embedded target. Then the erlang datasets are not off-loadable to the memory mapped files, rather they are very small in-memory tables. * erlang.c, aprx.h, telemetry.c, ax25.c, aprx-stat.c: Support for telemetry sending out info on received packets. * beacon.c: Improve input validation. 2008-02-18 Matti Aarnio - OH2MQK - KP20NG * VERSION: aprx-0.18 * aprx.8.in, aprx.conf.in, beacon.c: New syntax to define netbeacons. Also support older methods. * config.c: Bug in config_SKIPTEXT quoted string termination scanning. * netax25.c, aprsis.c, aprx.h: Removing dead code, hooks for future "TNC2 -> AX.25" * ttyreader.c: Send received KISS frames to system internal AX.25 network, if host has such (such as Linux) 2008-02-03 Matti Aarnio - OH2MQK - KP20NG * configure.in, netax25.c, autodetect header , and libutil function openpty(). * netax25.c, aprx.h, aprx.c: Rearranged netax25 module initing - to happen _latter_. * ttyreader.c, aprx.h, netax25.c: kissencoder() function * ttyreader.c, aprx.h, netax25.c: On Linux, use openpty() to create an AX.25pseudo-device on which we then can push AX.25 format packets received from non-AX.25 interfaces. This will itself also _ignore_ packets received from this created interface. * beacon.c: "for" attribute for beacon messages, thus this system can claim to be sending the beacons on behalf of others. * ax25.c, aprx.h: parse_ax25addr() function. * config.c, aprx.8.in, aprx.conf.in: The mycall parameter must be all uppercase AX.25 valid callsign, and must not be same as any other callsign in system internal AX.25 network. (This is meaningful only on Linux systems..) * Makefile.in: Just some cleanup 2008-01-30 Matti Aarnio - OH2MQK - KP20NG * aprsis.c: Spotted watchdog doing reconnects every 2 minutes (like it is supposed to be), and realized that it really should not care what the server says, just that server is saying something... * erlang.c: A bug in backing-store map protection logic. * aprx.c: See if pidfile exists. If it does and the start is not for foreground, refuse to run if process given in the pidfile does exist. * aprx.8.in, config.c, netax25.c, ttyreader.c, aprsis.c, aprx.h, ax25.c, aprx.conf.in, beacon.c: New "callsign" config parameter for "radio serial" ports. messages received from given port are sent out using that callsign. Revamp the whole config parsing. * aprsis.c: Smarter main-loop to aprs-is loop message pass preparation and usage codes -- pass a struct block as message leader. * ttyreader.c, netax25.c, aprsis, ax25.c, beacon.c, aprx.h: Pass explicite parameter towards the APRSIS telling, which callsign (radio receiver) the message came in from. * aprx.h, config.c, ttyreader.c, beacon.c: Rework config parameter line parsing. Just one param is processed by the main config reader loop, any further are tasks of the individual subroutines (ttyreader, beacon) 2008-01-26 Matti Aarnio - OH2MQK - KP20NG * erlang.c: Properly handle r/o share mapping of the aprx.state erlang dataset. This mode is used by the aprx-stat program. * netax25.c, aprx.conf.in, aprx.8.in: For each AX.25 socket received packet, query reception interface address and reformat it to TNC2 format. Use that for reception reports, logs, filters etc. Reason: port names given in /etc/ax25/axports do not persist at all! Even clean system boot may yield different port names than what the file lists. * Version 0.17 * INSTALL, README: Minor edits * configure.in, Makefile.in, debian/rules, rpm/aprx.spec.in, Makefile: A bit more coherency on make system regarding linking. Remove generated Makefile. (Keep generated configure !) * config.c, ttyreader.c, aprx.conf.in, aprx.8.in: Call the "serialport" now with name "radio". (Also old one works, so old config does not break.) * aprx.h, netax25.c, ax25.c, ttyreader.c: Pass port name down to tnc2_rxgate() function where it is used in rf.log outputs. A distributed multi- receiver setup log is somewhat .. odd looking when most receivers get same packet. * logrotate.arpx.in: Rotate weekly, and compress immediately. Monthly turned out to be too much for "embedded" OH1GSM-1 system. 2008-01-12 Matti Aarnio - OH2MQK - KP20NG * configure.in, configure, debian/rules, rpm/aprx.spec.in, rpm/aprx.init, Makefile, config.h.in: More RPM / configure rework. * configure.in, configure, Makefile.in, Makefile, config.h.in, install-sh, rpm/aprx.spec.in, debian/rules: Had to add minimalistic configure script into system for the RPM multi-target compilation to work. * Makefile, rpm/aprx.spec.in, rpm/aprx.default, rpm/aprx.init: RPM package build framework, including init and logrotate -scripts 2008-01-10 Matti Aarnio - OH2MQK - KP20NG * version 0.16 * erlang.c: Make the logged data narrower - to usually fit in 80 char lines. * aprx.c: Always close STDIN from reading, and replace it with a file handle opened on /dev/null. When daemoning, close also STDOUT and STDERR, and replace them with handles writing to /dev/null. .. and do it as late as possible. * aprx.h, aprx.c, config.c, erlang.c, aprx.8.in, aprx.conf.in, aprx-stat.c: Add (and document) option for logging erlang data on separate file without any runtime options or need to divert stdout or syslog anywhere. 2008-01-08 Matti Aarnio - OH2MQK - KP20NG * aprx.h, ttyreader.c, erlang.c, netax25.c, aprx-stat.c: When talking with multi-drop KISS, account each TNC separately. 2008-01-07 Matti Aarnio - OH2MQK - KP20NG * version 0.15 * erlang.c: Do not double-open the state backingstore file * aprsis.c: Remember to close the opened log files. * ttyreader.c: Support case of _no_ serialports (reading only via AX.25 net) * Makefile, logrotate.aprx.in: Put aprx logs on /var/log/aprx/, and have monthly rotate. * debian/*, Makefile: Debian package building infrastructure * aprx.8.in, aprx.conf.in: A bit elaboration on how to add multiple entries of aprsis-server, and serialport definitions. * aprsis.c, ttyreader.c, aprx.8.in, aprx.conf.in: Remove last vestiges of program having any hardwired limits on number of anything. There are (of course) lots of parameters that are singletons, but all multiples are now unlimited (within memory limits..) * Makefile: "make install" does install the CFGFILE (aprx.conf) if it does not have to overwrite the thing. "make dist" is even more interesting beast.. 2008-01-06 Matti Aarnio - OH2MQK - KP20NG * version 0.14 * ttyreader.c, aprx.conf.in, aprx.8.in: Support TNC2 monitor format on reception. * ttyreader.c, aprx.conf.in, aprx.8.in: Overload the "serialport" configuration option with a mechanism to define a IP-literal addressable remote TCP port somewhere with e.g. KISS TNC on it. 2008-01-05 Matti Aarnio - OH2MQK - KP20NG * Makefile: Radical revisioning, Best Current Practice * aprx.c: Write aprx.pid file in all cases, not only when starting as daemon. * version 0.13 * aprx.h, aprx.c, erlang.c, aprx-stat.c: Modifications on shared memory segment head, stores MYCALL, correct running process PID. * INSTALL: Some text fixes * aprx-stat.c, aprx.h, erlang.c: Report also server process PID on the SNMP dataset, and time in seconds since it was started. * ax25.c: Verify TNC2 format APRS message's FROM>DEST,VIA,VIA: callsigns to be of proper syntax. * Makefile, aprx.8, aprx.8.in, aprx-stat.8, aprx-stat.8.in, aprx.conf, aprx.conf.in: Centralized a bit of configuring into Makefile, generating files from *.in versions. * aprxpolls.c: Library function used everywhere, part of "eliminate fixed size preallocations" -task. * aprsis.c: Debug logging improvements, parent death detection. Preserved MAXAPRSIS setting - of 10 servers. * ttyreader.c: Unfix the number of TTYs, now can be as many as one wants. * config.c, aprx.c, aprx.h, aprx.conf.in, aprx.8.in: pidfile configuration parameter, and its usage 2008-01-04 Matti Aarnio - OH2MQK - KP20NG * VERSION, arpx.c, Makefile, aprx.8, aprx.8.in, aprx-stat.8, aprx-stat.8.in: Define system version in VERSION file, have it stamped on programs and packages. * config.c: The "aprxlog" and "rflog" parameters were interchanged at some point. * aprx.c, aprsis.c, beacon.c: Complain loudly with -d or -v options on, and if the configuration does not set global mycall parameter. Do not refuse to run, though! This is perfectly valid for things like Erlang-monitoring. 2007-12-29 Matti Aarnio - OH2MQK - KP20NG * version aprx-v0.12 * PROTOCOLS: Writeup of existing (and planned) protocols that this software uses. * ax25.c: Correct processing of 3rd-party frames. Also separate TNC2 formatted frame Rx-igateing rules from AX.25-to-TNC2 format translation routine. * ax25.c, beacon.c, aprsis.c: Centralize the APRSIS communication line ending CRLF char pair addition into common code, not distributed all over. 2007-12-25 Matti Aarnio - OH2MQK - KP20NG * version aprx-v0.11 * TODO, README: Cleanup * config.c, netax25.c, aprx.8, aprx.conf: Config option "ax25-rxport" - Limits acceptance of APRS packets only from listed Linux AX.25 ports. * ttyreader.c, aprx.conf, aprx.8: Reworked a bit of the serialport config options. Now initstring, and various KISS modes are configurable * erlang.c: Carefull approach on erlang-file opening, if file exists, and is non-zero size, open it only if magics match. * config.c: config_SKIPTEXT() terminates all scanned strings with NUL byte, and moves to byte following it, if the termination byte was not a NUL byte originally. * aprx-stat.c, aprx-stat.8: Option -t to show timestamps differently. * Makefile, man-to-html.sh: Produce decent format HTML versions of the man-pages. * config.c, erlang.c, aprx.8, aprx.conf: Added "erlang-log1min" option to control 1 minute interval Erlang sampling logging behaviour. * Makefile: Fix "make install" of man-pages * version aprx-v0.10 * aprx-stat.c, aprx-stat.8, erlang.c, Makefile: Statistics reporter tool. * aprsis.c, config.c, aprx.8, aprx.conf: Multiple aprsis-server config definitions are now supported, and used in round-robin fashion. * ax25.c, config.c, aprx.8, aprx.conf: Config option ax25-filter * ttyreader.c, erlang.c: Magic channel capacity constant expressions updated.. 2007-12-23 Matti Aarnio - OH2MQK - KP20NG * aprx.c, aprx.h, aprsis.c, aprx.8, aprx.conf, ax25.c, erlang.c: Multiple configuration file options, rf-log, and aprx-log -files, related documentation. * config.c, aprx.8: Special quoted-string escape processing. * version aprx-v0.08 * erlang.c, aprx.c, aprx.h: Erlang data has now a mmap():ed filesystem based backing-store. Erlang-data can be syslog()ed, and independently of that, it can be printed on STDOUT. Default syslog facility is "NONE". * Great Rename -- the thing is now called: aprx 2007-12-06 Matti Aarnio - OH2MQK - KP20NG * aprsg.8, erlang.c, aprsg.c, netax25.c, ttyreader.c: Erlang logging now uses syslog(3), unless explicitely told to use output to stdout. Also the Erlang log format was altered a bit, now it reports also number of packets in the interval. * erlang.c, Makefile: "make ERLANG1=1" compiles in also 1 minute erlang logging interval. * aprsis.c: Code refactored to put the APRS-IS communication into its own fork()ed sub-process communicating via a socketpair() with the main loop. Now reconnection time with the APRS-IS server does not affect functionality of the main loop. 2007-12-05 Matti Aarnio - OH2MQK - KP20NG * ChangeLog Opened for the first time. Version 0.06. aprx-2.08.svn593/dprsgw.c0000644000175000017500000006637212305424775014156 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" #ifndef DISABLE_IGATE /* * The DPRS RX Gateway * * Receive data from DPRS. * Convert to 3rd-party frame. * Send out to APRSIS and Digipeaters. * * http://www.aprs-is.net/DPRS.aspx * * * * GPSxyz -> APRS symbols mapping: * * http://www.aprs.org/symbols/symbolsX.txt */ typedef struct dprsgw_history { time_t gated; char callsign[10]; } dprsgw_history_t; // Up to 30 history entries to not to send same callsign too often #define HISTORYSIZE 30 typedef struct dprs_gw { char *ggaline; char *rmcline; int ggaspace; int rmcspace; int historylimit; // Time limit in seconds dprsgw_history_t history[HISTORYSIZE]; } dprsgw_t; // The dprslog() logs ONLY when '-d' mode is running. // .. and it will be removed soon. const char *dprslogfile; void dprslog( const time_t stamp, const uint8_t *buf ) { if (dprslogfile == NULL) return; // Nothing to do FILE *fp = fopen(dprslogfile,"a"); if (fp != NULL) { fprintf(fp, "%ld\t%s\n", stamp, (const char *)buf); fclose(fp); } } static void dprsgw_flush(dprsgw_t *dp) { if (dp->ggaline == NULL) { dp->ggaspace = 200; dp->ggaline = malloc(200); } if (dp->rmcline == NULL) { dp->rmcspace = 200; dp->rmcline = malloc(200); } dp->ggaline[0] = 0; dp->rmcline[0] = 0; } static void *dprsgw_new(int historylimit) { dprsgw_t *dp = calloc(1, sizeof(*dp)); dp->historylimit = historylimit; dprsgw_flush(dp); // init buffers return dp; } // Ratelimit returns 0 for "can send", 1 for "too soon" static int dprsgw_ratelimit( dprsgw_t *dp, const void *tnc2buf ) { int i, n; char callsign[10]; time_t expiry = tick.tv_sec - dp->historylimit; memcpy(callsign, tnc2buf, sizeof(callsign)); callsign[sizeof(callsign)-1] = 0; for (i = 0; i < sizeof(callsign); ++i) { char c = callsign[i]; if (c == '>') { callsign[i] = 0; break; } } n = -1; for (i = 0; i < HISTORYSIZE; ++i) { // Is there an entry? if (dp->history[i].callsign[0] == 0) continue; if ((dp->history[i].gated - tick.tv_sec) > 0) { // system time has jumped backwards, expire it. dp->history[i].gated = expiry; } if ((dp->history[i].gated - expiry) > 0) { // Fresh enough to be interesting! if (strcmp(dp->history[i].callsign, callsign) == 0) { // This callsign! return 1; } } else { dp->history[i].callsign[0] = 0; // discard it.. if (n < 0) n = i; // save first free slot's index } } if (n >= 0) { memcpy(dp->history[n].callsign, callsign, sizeof(callsign)); dp->history[n].gated = tick.tv_sec; } return 0; } typedef struct gps2apr_syms { const char gps[3]; const char aprs[3]; int flags; } gps2aprs_syms_t; // FIXME: Some symbols have 3 characters, // others take 3rd as overlay... // Add control flags below! static const gps2aprs_syms_t gps2aprsSyms[] = { { "A0", "\\0", 0 }, { "A1", "\\1", 0 }, { "A2", "\\2", 0 }, { "A3", "\\3", 0 }, { "A4", "\\4", 0 }, { "A5", "\\5", 0 }, { "A6", "\\6", 0 }, { "A7", "\\7", 0 }, { "A8", "\\8", 0 }, { "A9", "\\9", 0 }, { "AA", "\\A", 0 }, { "AB", "\\B", 0 }, { "AC", "\\C", 0 }, { "AD", "\\D", 0 }, { "AE", "\\E", 0 }, { "AF", "\\F", 0 }, { "AG", "\\G", 0 }, { "AH", "\\H", 0 }, { "AI", "\\I", 0 }, { "AJ", "\\J", 0 }, { "AK", "\\K", 0 }, { "AL", "\\L", 0 }, { "AM", "\\M", 0 }, { "AN", "\\N", 0 }, { "AO", "\\O", 0 }, { "AP", "\\P", 0 }, { "AQ", "\\Q", 0 }, { "AR", "\\R", 0 }, { "AS", "\\S", 0 }, { "AT", "\\T", 0 }, { "AU", "\\U", 0 }, { "AV", "\\V", 0 }, { "AW", "\\W", 0 }, { "AX", "\\X", 0 }, { "AY", "\\Y", 0 }, { "AZ", "\\Z", 0 }, { "BB", "/!", 0 }, { "BC", "/\"", 0 }, { "BD", "/#", 0 }, { "BE", "/$", 0 }, { "BF", "/%", 0 }, { "BG", "/&", 0 }, { "BH", "/'", 0 }, { "BI", "/(", 0 }, { "BJ", "/)", 0 }, { "BK", "/*", 0 }, { "BL", "/+", 0 }, { "BM", "/,", 0 }, { "BN", "/-", 0 }, { "BO", "/.", 0 }, { "BP", "//", 0 }, { "DS", "\\[", 0 }, { "DT", "\\\\", 0 }, { "DU", "\\]", 0 }, { "DV", "\\^", 0 }, { "DW", "\\_", 0 }, { "DX", "\\`", 0 }, { "HS", "/[", 0 }, { "HT", "/\\", 0 }, { "HU", "/]", 0 }, { "HV", "/^", 0 }, { "HW", "/_", 0 }, { "HX", "/`", 0 }, { "J1", "/{", 0 }, { "J2", "/|", 0 }, { "J3", "/}", 0 }, { "J4", "/~", 0 }, { "LA", "/a", 0 }, { "LB", "/b", 0 }, { "LC", "/c", 0 }, { "LD", "/d", 0 }, { "LE", "/e", 0 }, { "LF", "/f", 0 }, { "LG", "/g", 0 }, { "LH", "/h", 0 }, { "LI", "/i", 0 }, { "LJ", "/j", 0 }, { "LK", "/k", 0 }, { "LL", "/l", 0 }, { "LM", "/m", 0 }, { "LN", "/n", 0 }, { "LO", "/o", 0 }, { "LP", "/p", 0 }, { "LQ", "/q", 0 }, { "LR", "/r", 0 }, { "LS", "/s", 0 }, { "LT", "/t", 0 }, { "LU", "/u", 0 }, { "LV", "/v", 0 }, { "LW", "/w", 0 }, { "LX", "/x", 0 }, { "LY", "/y", 0 }, { "LZ", "/z", 0 }, { "MR", "/:", 0 }, { "MS", "/;", 0 }, { "MT", "/<", 0 }, { "MU", "/=", 0 }, { "MV", "/>", 0 }, { "MW", "/?", 0 }, { "MX", "/@", 0 }, { "NR", "\\:", 0 }, { "NS", "\\;", 0 }, { "NT", "\\<", 0 }, { "NU", "\\=", 0 }, { "NV", "\\>", 0 }, { "NW", "\\?", 0 }, { "NX", "\\@", 0 }, { "OB", "\\!", 0 }, { "OC", "\\\"", 0 }, { "OD", "\\#", 0 }, { "OE", "\\$", 0 }, { "OF", "\\%", 0 }, { "OG", "\\&", 0 }, { "OH", "\\'", 0 }, { "OI", "\\(", 0 }, { "OJ", "\\)", 0 }, { "OK", "\\*", 0 }, { "OL", "\\+", 0 }, { "OM", "\\,", 0 }, { "ON", "\\-", 0 }, { "OO", "\\.", 0 }, { "OP", "\\/", 0 }, { "P0", "/0", 0 }, { "P1", "/1", 0 }, { "P2", "/2", 0 }, { "P3", "/3", 0 }, { "P4", "/4", 0 }, { "P5", "/5", 0 }, { "P6", "/6", 0 }, { "P7", "/7", 0 }, { "P8", "/8", 0 }, { "P9", "/9", 0 }, { "PA", "/A", 0 }, { "PB", "/B", 0 }, { "PC", "/C", 0 }, { "PD", "/D", 0 }, { "PE", "/E", 0 }, { "PF", "/F", 0 }, { "PG", "/G", 0 }, { "PH", "/H", 0 }, { "PI", "/I", 0 }, { "PJ", "/J", 0 }, { "PK", "/K", 0 }, { "PL", "/L", 0 }, { "PM", "/M", 0 }, { "PN", "/N", 0 }, { "PO", "/O", 0 }, { "PP", "/P", 0 }, { "PQ", "/Q", 0 }, { "PR", "/R", 0 }, { "PS", "/S", 0 }, { "PT", "/T", 0 }, { "PU", "/U", 0 }, { "PV", "/V", 0 }, { "PW", "/W", 0 }, { "PX", "/X", 0 }, { "PY", "/Y", 0 }, { "PZ", "/Z", 0 }, { "Q1", "\\{", 0 }, { "Q2", "\\|", 0 }, { "Q3", "\\}", 0 }, { "Q4", "\\~", 0 }, { "SA", "\\a", 0 }, { "SB", "\\b", 0 }, { "SC", "\\c", 0 }, { "SD", "\\d", 0 }, { "SE", "\\e", 0 }, { "SF", "\\f", 0 }, { "SG", "\\g", 0 }, { "SH", "\\h", 0 }, { "SI", "\\i", 0 }, { "SJ", "\\j", 0 }, { "SK", "\\k", 0 }, { "SL", "\\l", 0 }, { "SM", "\\m", 0 }, { "SN", "\\n", 0 }, { "SO", "\\o", 0 }, { "SP", "\\p", 0 }, { "SQ", "\\q", 0 }, { "SR", "\\r", 0 }, { "SS", "\\s", 0 }, { "ST", "\\t", 0 }, { "SU", "\\u", 0 }, { "SV", "\\v", 0 }, { "SW", "\\w", 0 }, { "SX", "\\x", 0 }, { "SY", "\\y", 0 }, { "SZ", "\\z", 0 }, }; static int gps2aprs_syms_count = sizeof(gps2aprsSyms) / sizeof(gps2aprsSyms[0]); static int parse_gps2aprs_symbol(const char *gpsxxx, char *aprssymbol) { int i, mid, high, low; char gps[3]; gps[0] = gpsxxx[0]; gps[1] = gpsxxx[1]; gps[2] = 0; low = 0; high = gps2aprs_syms_count-1; while (low < high) { mid = (low + high) >> 1; // divide by 2 i = strcmp(gps, gps2aprsSyms[mid].gps); // if (debug) printf("GPS2APRS: '%s' '%s', low=%d mid=%d high=%d\n",gps, gps2aprsSyms[mid].gps, low, mid, high); if (i == 0) { // Exact match char c3 = gpsxxx[2]; strcpy(aprssymbol, gps2aprsSyms[mid].aprs); if (c3 != 0 && c3 != ' ' && aprssymbol[0] != '/') { // FIXME: overlay ??? aprssymbol[0] = c3; } return 0; } if (i > 0) { low = mid+1; } else { high = mid-1; } } return i; } // The "Specification" says to use this checksum method.. // It uses right-left inverted version of the polynome // of CCITT-CRC-16 but in the end it INVERTS the result! // Thus the result is NOT CCITT-CRC-16, but something // uniquely ICOM D-STAR.. /* static int dprsgw_crccheck( const uint8_t *s, int len ) { int icomcrc = 0xffff; for ( ; len > 0; ++s, --len) { uint8_t ch = *s; int i; for (i = 0; i < 8; i++) { int xorflag = (icomcrc ^ ch) & 0x01; icomcrc >>= 1; if (xorflag) icomcrc ^= 0x8408; ch >>= 1; } } return (~icomcrc) & 0xffff; } */ static int dprsgw_isvalid( struct serialport *S ) { int i; if (S->rdlinelen < 20) { if (debug) printf("Too short a line for DPRS"); return 0; // definitely not! } if (memcmp("$$CRC", S->rdline, 5) == 0 && S->rdline[9] == ',') { // Maybe a $$CRCB727,OH3BK-D>APRATS,DSTAR*:@165340h6128.23N/02353.52E-D-RATS (GPS-A) /A=000377 int crc; int csum = -1; int crc16; S->rdline[S->rdlinelen] = '\r'; crc16 = calc_crc_ccitt(0xFFFF, S->rdline+10, S->rdlinelen+1-10); // INCLUDE the CR on CRC calculation! crc = (crc16 ^ 0xFFFF); // Output is INVERTED S->rdline[S->rdlinelen] = 0; i = sscanf((const char*)(S->rdline), "$$CRC%04x,", &csum); if (i != 1 || csum != crc) { if (debug) printf("Bad DPRS APRS CRC: l=%d, i=%d, %04x/%04x vs. %s\n", S->rdlinelen, i, crc, csum, S->rdline); // return 0; } else { if (debug>1) printf("$$CRC DSTAR=%04x CCITT-X25-FCS=%04x\n", csum, crc16); if (debug) printf("Good DPRS APRS CRC: l=%d, i=%d, %04x/%04x vs. %s\n", S->rdlinelen, i, crc, csum, S->rdline); return 1; } return 0; } else if (memcmp("$GP", S->rdline, 3) == 0) { // Maybe $GPRMC,170130.02,A,6131.6583,N,02339.1552,E,0.00,154.8,290510,6.5,E,A*02 ? int xor = 0; int csum = -1; char c; // if (debug) printf("NMEA: '%s'\n", S->rdline); for (i = 1; i < S->rdlinelen; ++i) { c = S->rdline[i]; if (c == '*' && (i >= S->rdlinelen - 3)) { break; } xor ^= c; } xor &= 0xFF; if (i != S->rdlinelen -3 || S->rdline[i] != '*') return 0; // Wrong place to stop if (sscanf((const char *)(S->rdline+i), "*%02x%c", &csum, &c) != 1) { return 0; // Too little or too much } if (xor != csum) { if (debug) printf("Bad DPRS $GP... checksum: %02x vs. %02x\n", csum, xor); return 0; } return 1; } else { int xor = 0; int csum = -1; char c; // .. uh? maybe? Precisely 29 characters: // "OH3KGR M, " if (S->rdlinelen != 29 || S->rdline[8] != ',') { if (debug) printf("Bad DPRS identification(?) packet - length(%d) != 29 || line[8] != ',': %s\n", S->rdlinelen, S->rdline); return 0; } if (debug) printf("DPRS NMEA: '%s'\n", S->rdline); for (i = 0; i < S->rdlinelen; ++i) { c = S->rdline[i]; if (c == '*') { break; } xor ^= c; } xor &= 0xFF; if (sscanf((const char *)(S->rdline+i), "*%x%c", &csum, &c) < 1) { if (memcmp(S->rdline+8, ", ", 21) == 0) { if (debug) printf("DPRS IDENT LINE OK: '%s'\n", S->rdline); return 1; } // if (debug) printf("csum bad NMEA: '%s'\n", S->rdline); return 0; // Too little or too much } if (xor != csum) { if (debug) printf("Bad DPRS IDENT LINE checksum: %02x vs. %02x\n", csum, xor); return 0; } if (debug) printf("DPRS IDENT LINE OK: '%s'\n", S->rdline); // if (debug) printf("csum valid NMEA: '%s'\n", S->rdline); return 1; // Maybe valid? } } // Split NMEA text line at ',' characters static int dprsgw_nmea_split(char *nmea, char *fields[], int n) { int i = 0; --n; fields[i] = nmea; for ( ; *nmea; ++nmea ) { for ( ; *nmea != 0 && *nmea != ','; ++nmea ) ; if (*nmea == 0) break; // THE END! if (*nmea == ',') *nmea++ = 0; // COMMA terminates a field, change to SPACE if (i < n) ++i; // Prep next field index fields[i] = nmea; // save field pointer } fields[i] = NULL; return i; } static void dprsgw_nmea_igate( const struct aprx_interface *aif, const uint8_t *ident, dprsgw_t *dp ) { int i; char *gga[20]; char *rmc[20]; char tnc2buf[2000]; int tnc2addrlen; int tnc2buflen; char *p, *p2; const char *p0; const char *s; int alt_feet = -9999999; char aprssym[3]; if (debug) { printf(" DPRS: ident='%s', GGA='%s', RMC='%s'\n", ident, dp->ggaline, dp->rmcline); } strcpy(aprssym, "/>"); // Default.. parse_gps2aprs_symbol((const char *)ident+9, aprssym); memset(gga, 0, sizeof(gga)); memset(rmc, 0, sizeof(rmc)); // $GPGGA,hhmmss.dd,xxmm.dddd,,yyymm.dddd,,v, // ss,d.d,h.h,M,g.g,M,a.a,xxxx*hh // $GPRMC,hhmmss.dd,S,xxmm.dddd,,yyymm.dddd,, // s.s,h.h,ddmmyy,d.d, ,M*hh // ,S, = Status: 'A' = Valid, 'V' = Invalid if (dp->ggaline[0] != 0) dprsgw_nmea_split(dp->ggaline, gga, 20); if (dp->rmcline[0] != 0) dprsgw_nmea_split(dp->rmcline, rmc, 20); if (rmc[2] != NULL && strcmp(rmc[2],"A") != 0) { if (debug) printf("Invalid DPRS $GPRMC packet (validity='%s')\n", rmc[2]); return; } if (gga[6] != NULL && strcmp(gga[6],"1") != 0) { if (debug) printf("Invalid DPRS $GPGGA packet (validity='%s')\n", gga[6]); return; } if (dp->ggaline[0] == 0 && dp->rmcline[0] == 0) { if (debug) printf("No DPRS $GPRMC nor $GPGGA packets available.\n"); return; } p = tnc2buf; for (i = 0; i < 8; ++i) { if (ident[i] == ' ') { *p++ = '-'; for ( ; ident[i+1] == ' ' && i < 8; ++i ); continue; } *p++ = ident[i]; } if (p > tnc2buf && p[-1] == '-') --p; p += sprintf(p, ">APDPRS,DSTAR*"); tnc2addrlen = p - tnc2buf; *p++ = ':'; *p++ = '!'; // Std position w/o messaging p2 = p; if (gga[2] != NULL) { s = gga[2]; } else if (rmc[3] != NULL) { s = rmc[3]; } else { // No coordinate! if (debug) printf("dprsgw: neither GGA nor RMC coordinates available, discarding!\n"); return; } p0 = strchr(s, '.'); if (!p0) { if (debug) printf("dprsgw: invalid format NMEA North coordinate: '%s'\n", s); return; } while (p0 - s < 4) { *p++ = '0'; ++p0; // move virtually } p += sprintf(p, "%s", s); if (p2+7 < p) { // Too long! p = p2+7; } while (p2+7 > p) { // Too short! *p++ = ' '; // unprecise position } if (gga[2] != NULL) { s = gga[3]; // } else if (rmc[3] != NULL) { s = rmc[4]; // } p += sprintf(p, "%s", s); *p++ = aprssym[0]; p2 = p; if (gga[2] != NULL) { s = gga[4]; // yyymm.dddd } else if (rmc[3] != NULL) { s = rmc[5]; // yyymm.dddd } p0 = strchr(s, '.'); if (!p0) { if (debug) printf("dprsgw: invalid format NMEA East coordinate: '%s'\n", s); return; } while (p0 - s < 5) { *p++ = '0'; ++p0; // move virtually } p += sprintf(p, "%s", s); if (p2+8 < p) { // Too long! p = p2+8; } while (p2+8 > p) { // Too short! *p++ = ' '; // unprecise position } if (gga[2] != NULL) { p += sprintf(p, "%s", gga[5]); // } else if (rmc[3] != NULL) { p += sprintf(p, "%s", rmc[6]); // } *p++ = aprssym[1]; // DPRS: ident='OH3BK D,BN *59 ', GGA='$GPGGA,204805,6128.230,N,2353.520,E,1,3,0,115,M,0,M,,*6d', RMC='' // DPRSGW GPS data: OH3BK-D>APDPRS,DSTAR*:!6128.23N/02353.52E> if (gga[2] != NULL) { if (gga[9] != NULL && gga[9][0] != 0) alt_feet = strtol(gga[9], NULL, 10); if (strcmp(gga[10],"M") == 0) { // Meters! Convert to feet.. alt_feet = (10000 * alt_feet) / 3048; } else { // Already feet - presumably } } // FIXME: more data! // RMC HEADING/SPEED p0 = (const char *)ident + 29; s = (const char *)ident + 9+4; p2 = p; for ( ; s < p0; ++s ) { if (*s != ' ') break; } if (s < p0) *p++ = ' '; for ( ; s < p0; ++s ) { if (*s == '*') break; *p++ = *s; } if (p > p2) *p++ = ' '; if (alt_feet > -9999999) { p += sprintf(p, "/A=%06d", alt_feet); } *p = 0; tnc2buflen = p - tnc2buf; if (debug) printf("DPRSGW GPS data: %s\n", tnc2buf); if (!dprsgw_ratelimit( dp, tnc2buf )) { char *fromcall, *origtocall; char *b; if (aif != NULL) { igate_to_aprsis( aif->callsign, 0, (const char *)tnc2buf, tnc2addrlen, tnc2buflen, 0, 0); // Bytes have been counted previously, now count meaningful packet erlang_add(aif->callsign, ERLANG_RX, 0, 1); } fromcall = tnc2buf; p = fromcall; origtocall = NULL; while (*p != '>' && *p != 0) ++p; if (*p == '>') { *p++ = 0; origtocall = p; } else return; // BAD :-( p = origtocall; while (p != NULL && *p != ':' && *p != 0 && *p != ',') ++p; if (p != NULL && (*p == ':' || *p == ',')) { *p++ = 0; } b = tnc2buf + tnc2addrlen +1; interface_receive_3rdparty( aif, fromcall, origtocall, "DSTAR*", b, tnc2buflen - (b-tnc2buf) ); } } static void dprsgw_rxigate( struct serialport *S ) { const struct aprx_interface *aif = S->interface[0]; uint8_t *tnc2addr = S->rdline; int tnc2addrlen = S->rdlinelen; uint8_t *tnc2body = S->rdline; int tnc2bodylen = S->rdlinelen; #ifndef DPRSGW_DEBUG_MAIN if (aif == NULL) { if (debug) printf("OOPS! NO ON DPRS SERIAL PORT! BUG!\n"); return; } #endif if (memcmp("$$CRC", tnc2addr, 5) == 0 && tnc2addrlen > 20) { tnc2addr += 10; tnc2addrlen -= 10; tnc2bodylen -= 10; // header + body together tnc2body = memchr( tnc2addr, ':', tnc2addrlen); if (tnc2body != NULL) { char *fromcall, *origtocall; char *s; tnc2addrlen = tnc2body - tnc2addr; ++tnc2body; if (dprsgw_ratelimit(S->dprsgw, tnc2addr)) { // Rate-limit ordered rejection return; } // Acceptable packet, Rx-iGate it! igate_to_aprsis( aif->callsign, 0, (const char *)tnc2addr, tnc2addrlen, tnc2bodylen, 0, 0); // Bytes have been counted previously, now count meaningful packet erlang_add( aif->callsign, ERLANG_RX, 0, 1 ); fromcall = (char*)tnc2addr; s = fromcall; origtocall = NULL; while (*s != '>' && *s != 0) ++s; if (*s == '>') { *s++ = 0; origtocall = s; } else return; // BAD :-( s = origtocall; while (s != NULL && *s != ':' && *s != 0 && *s != ',') ++s; if (s != NULL && (*s == ':' || *s == ',')) { *s++ = 0; } interface_receive_3rdparty( aif, fromcall, origtocall, "DSTAR*", (const char*)tnc2body, tnc2bodylen - (tnc2body-tnc2addr) ); return; } else { // Bad packet! if (debug) printf("Bad DPRS packet! %s\n", S->rdline); return; } } else if (memcmp("$GPGGA,", tnc2addr, 7) == 0) { dprsgw_t *dp = S->dprsgw; if (dp->ggaspace <= S->rdlinelen) { dp->ggaline = realloc(dp->ggaline, S->rdlinelen+1); dp->ggaspace = S->rdlinelen; } memcpy(dp->ggaline, tnc2addr, tnc2bodylen); dp->ggaline[tnc2bodylen] = 0; if (debug) printf("DPRS GGA: %s\n", dp->ggaline); } else if (memcmp("$GPRMC,", tnc2addr, 7) == 0) { dprsgw_t *dp = S->dprsgw; if (dp->rmcspace <= S->rdlinelen) { dp->rmcline = realloc(dp->rmcline, S->rdlinelen+1); dp->rmcspace = S->rdlinelen; } memcpy(dp->rmcline, tnc2addr, tnc2bodylen); dp->rmcline[tnc2bodylen] = 0; if (debug) printf("DPRS RMC: %s\n", dp->rmcline); } else if (tnc2addr[8] == ',' && tnc2bodylen == 29) { // Acceptable DPRS "ident" line dprsgw_t *dp = S->dprsgw; tnc2addr[tnc2bodylen] = 0; // zero-terminate just in case dprsgw_nmea_igate(aif, tnc2addr, dp); } else { // this should never be called... if (debug) printf("Unrecognized DPRS packet: %s\n", S->rdline); return; } } /* * Receive one text line from serial port * It will end with 0x00 byte, and not contain \r nor \n. * * It MAY have junk at the start. * * */ static void dprsgw_receive( struct serialport *S ) { int i; uint8_t *p; if (debug) dprslog(S->rdline_time, S->rdline); do { if (dprsgw_isvalid(S)) { // Feed it to DPRS-APRS-GATE dprsgw_rxigate( S ); return; // Done! } else { // Not a good packet! See if there is a good packet inside? dprsgw_flush(S->dprsgw); // bad input -> discard accumulated data p = memchr(S->rdline+1, '$', S->rdlinelen-1); if (p == NULL) break; // No '$' to start something i = S->rdlinelen - (p - S->rdline); if (i <= 0) break; // exhausted! S->rdlinelen = i; memcpy(S->rdline, p, S->rdlinelen); S->rdline[i] = 0; continue; } } while(1); } /* * Receive more data from DPRS type serial port * This handles correct data accumulation and sync hunting */ int dprsgw_pulldprs( struct serialport *S ) { const time_t rdtime = S->rdline_time; const struct aprx_interface *aif = S->interface[0]; int c; int i; // Account all received bytes, this may or may not be a packet erlang_add(aif->callsign, ERLANG_RX, S->rdlinelen, 0); if (S->dprsgw == NULL) S->dprsgw = dprsgw_new(30); // FIXME: hard-coded 30 second delay for DPRS repeats if ((rdtime+2 - tick.tv_sec) < 0) { // A timeout has happen? (2 seconds!) Either data is added constantly, // or nothing was received from DPRS datastream! if (S->rdlinelen > 0) if (debug)printf("dprsgw: previous data is %d sec old, discarding its state: %s\n",((int)(tick.tv_sec-rdtime)), S->rdline); S->rdline[S->rdlinelen] = 0; if (S->rdlinelen > 0 && debug) dprslog(rdtime, S->rdline); S->rdlinelen = 0; dprsgw_flush(S->dprsgw); // timeout -> discard accumulated data } S->rdline_time = tick.tv_sec; for (i=0 ; ; ++i) { c = ttyreader_getc(S); if (c < 0) { // if (debug) printf("dprsgw_pulldprs: read %d chars\n", i); return c; /* Out of input.. */ } if (debug>2) printf("DPRS %ld %3d %02X '%c'\n", tick.tv_sec, S->rdlinelen, c, c); /* S->dprsstate != 0: read data into S->rdline, == 0: discard data until CR|LF. Zero-size read line is discarded as well (only CR|LF on input frame) */ /* Looking for CR or LF.. */ if (c == '\n' || c == '\r') { /* End of line seen! */ if (S->rdlinelen > 0) { /* Non-zero-size string, put terminating 0 byte on it. */ S->rdline[S->rdlinelen] = 0; dprsgw_receive(S); } S->rdlinelen = 0; continue; } // A '$' starts possible data.. if (c == '$' && S->rdlinelen == 0) { S->rdline[S->rdlinelen++] = c; continue; } // More fits in? if (S->rdlinelen >= sizeof(S->rdline)-3) { // Too long a line... do { int len; uint8_t *p; dprsgw_flush(S->dprsgw); // Look for first '$' in buffer _after_ first char p = memchr(S->rdline+1, '$', S->rdlinelen-1); if (!p) { S->rdlinelen = 0; break; // Not found } len = S->rdlinelen - (p - S->rdline); if (len <= 0) { S->rdlinelen = 0; break; // exhausted } memcpy(S->rdline, p, len); S->rdline[len] = 0; S->rdlinelen = len; if (len >= 3) { if (memcmp("$$C", S->rdline, 3) != 0 && memcmp("$GP", S->rdline, 3) != 0) { // Not acceptable 3-char prefix // Eat away the collected prefixes.. continue; } } break; } while(1); } S->rdline[S->rdlinelen++] = c; /* // Too short to say anything? if (S->rdlinelen < 3) { continue; } if (S->rdlinelen == 3 && (memcmp("$$C", S->rdline, 3) != 0 && memcmp("$GP", S->rdline, 3) != 0)) { // No correct start, discard... dprsgw_flush(S->dprsgw); S->rdlinelen = 2; memcpy(S->rdline, S->rdline+1, 2); S->rdline[S->rdlinelen] = 0; if (S->rdline[0] != '$') { // Didn't start with a '$' S->rdlinelen = 0; } continue; } */ } /* .. input loop */ return 0; /* not reached */ } int dprsgw_prepoll(struct aprxpolls *app) { return 0; // returns number of sockets filled (ignored at caller) } int dprsgw_postpoll(struct aprxpolls *app) { return 0; // returns number of sockets filled (ignored at caller) } #ifdef DPRSGW_DEBUG_MAIN int freadln(FILE *fp, char *p, int buflen) // DPRSGW_DEBUG_MAIN { int n = 0; while (!feof(fp)) { int c = fgetc(fp); if (c == EOF) break; if (n >= buflen) break; *p++ = c; ++n; if (c == '\n') break; if (c == '\r') break; } return n; } int ttyreader_getc(struct serialport *S) // DPRSGW_DEBUG_MAIN { if (S->rdcursor >= S->rdlen) { /* Out of data ? */ if (S->rdcursor) S->rdcursor = S->rdlen = 0; /* printf("-\n"); */ return -1; } /* printf(" %02X", 0xFF & S->rdbuf[S->rdcursor++]); */ return (0xFF & S->rdbuf[S->rdcursor++]); } void igate_to_aprsis(const char *portname, const int tncid, const char *tnc2buf, int tnc2addrlen, int tnc2len, const int discard, const int strictax25_) // DPRSGW_DEBUG_MAIN { printf("DPRS RX-IGATE: %s\n", tnc2buf); } void interface_receive_3rdparty(const struct aprx_interface *aif, const char *fromcall, const char *origtocall, const char *gwtype, const char *tnc2data, const int tnc2datalen) // DPRSGW_DEBUG_MAIN { printf("DPRS 3RDPARTY RX: ....:}%s>%s,%s,GWCALLSIGN*:%s\n", fromcall, origtocall, gwtype, tnc2data); } int debug = 3; struct timeval tick; int main(int argc, char *argv[]) { struct serialport S; memset(&S, 0, sizeof(S)); #if 0 // A test where string has initially some incomplete data, then finally a real data printf("\nFIRST TEST\n"); strcpy((void*)S.rdline, "x$x4$GPPP$$$GP $$CRCB727,OH3BK-D>$$CRCB727,OH3BK-D>APRATS,DSTAR*:@165340h6128.23N/02353.52E-D-RATS (GPS-A) /A=000377"); S.rdlinelen = strlen((void*)S.rdline); dprsgw_receive(&S); printf("\nSECOND TEST\n"); strcpy((void*)S.rdline, "\304\3559\202\333$$CRCC3F5,OH3KGR-M>API282,DSTAR*:/123035h6131.29N/02340.45E>/IC-E2820"); S.rdlinelen = strlen((void*)S.rdline); dprsgw_receive(&S); printf("\nTHIRD TEST\n"); strcpy((void*)S.rdline, "[SOB]\"=@=@=@=>7\310=@\010!~~~~~~~!~~~~~~~\001\001\001\001\001\001\001\001[EOB]$$CRCBFB7,OH3BK>APRATS,DSTAR*:@124202h6128.23N/02353.52E-D-RATS (GPS-A) /A=000377"); S.rdlinelen = strlen((void*)S.rdline); dprsgw_receive(&S); printf("\nTEST 4.\n"); strcpy((void*)S.rdline, "$GPGGA,164829.02,6131.6572,N,02339.1567,E,1,08,1.1,111.3,M,19.0,M,,*61"); S.rdlinelen = strlen((void*)S.rdline); dprsgw_receive(&S); printf("\nTEST 5.\n"); strcpy((void*)S.rdline, "$GPRMC,170130.02,A,6131.6583,N,02339.1552,E,0.00,154.8,290510,6.5,E,A*02"); // strcpy((void*)S.rdline, "$GPRMC,164830.02,A,6131.6572,N,02339.1567,E,0.00,182.2,290510,6.5,E,A*07"); S.rdlinelen = strlen((void*)S.rdline); dprsgw_receive(&S); printf("\nTEST 6.\n"); strcpy((void*)S.rdline, "OH3BK D,BN *59 "); S.rdlinelen = strlen((void*)S.rdline); dprsgw_receive(&S); #endif S.ttyname = "testfile"; S.ttycallsign[0] = "OH2MQK-DR"; FILE *fp = fopen("tt.log", "r"); for (;;) { char buf1[3000]; int n = freadln(fp, buf1, sizeof(buf1)); if (n == 0) break; char *ep; tick.tv_sec = strtol(buf1, &ep, 10); // test code time init if (*ep == '\t') ++ep; int len = n - (ep - buf1); if (len > 0) { memcpy(S.rdbuf+S.rdlen, ep, len); S.rdlen += len; } if (S.rdlen > 0) dprsgw_pulldprs(&S); } fclose(fp); return 0; } #endif #endif aprx-2.08.svn593/aprx.spec0000644000175000017500000000601412320106142014273 0ustar colincolinName: aprx Version: 2.08.svn593 Release: 1%{?dist} Summary: Hamradio APRS iGate / Digipeater License: BSD URL: http://ham.zmailer.org/oh2mqk/aprx/ Source0: http://ham.zmailer.org/oh2mqk/aprx/%{name}-%{version}.tar.gz %if 0%{?rhel} >= 7 || 0%{?fedora} >= 16 BuildRequires: systemd-units %endif %if 0%{?fedora} >= 18 Requires(post): systemd Requires(preun): systemd Requires(postun): systemd %post %systemd_post %{name}.service %preun %systemd_preun %{name}.service %postun %systemd_postun_with_restart %{name}.service %else 0%{?fedora} = 17 Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units %post if [ $1 -eq 1 ] ; then # Initial installation /bin/systemctl daemon-reload >/dev/null 2>&1 || : fi %preun if [ $1 -eq 0 ] ; then # Package removal, not upgrade /bin/systemctl --no-reload disable aprx.service > /dev/null 2>&1 || : /bin/systemctl stop aprx.service > /dev/null 2>&1 || : fi %postun /bin/systemctl daemon-reload >/dev/null 2>&1 || : if [ $1 -ge 1 ] ; then # Package upgrade, not uninstall /bin/systemctl try-restart aprx.service >/dev/null 2>&1 || : fi %endif %description Aprx is an APRS iGate that has minimal system requirements. It can handle an arbitrary number of radio modems, optionally relay APRS packets from radio to the APRS-IS network, optionally digipeat AX25 with or without NEWn-N rules, optionally relay APRS packets from APRS-IS to radio (TX-iGate) %prep %setup -q %build %configure --with-erlangstorage CFLAGS="-m32 -march=i386" LDFLAGS="-m32 -march=i386" make %{?_smp_mflags} make logrotate.aprx %install mkdir -p $RPM_BUILD_ROOT/etc/sysconfig mkdir -p $RPM_BUILD_ROOT/etc/logrotate.d mkdir -p $RPM_BUILD_ROOT/var/log/aprx make install DESTDIR=$RPM_BUILD_ROOT install -m 644 logrotate.aprx $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/aprx install -m 644 rpm/aprx.default $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/aprx %if 0%{?rhel} >= 7 || 0%{?fedora} >= 16 mkdir -p $RPM_BUILD_ROOT%{_unitdir} install -m 644 rpm/aprx.service $RPM_BUILD_ROOT%{_unitdir}/%{name}.service %else mkdir -p $RPM_BUILD_ROOT%{_initddir} install -m 755 rpm/aprx.init $RPM_BUILD_ROOT%{_initddir}/aprx %endif %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) # INSTALL not bundled %doc LICENSE README TODO PROTOCOLS %doc ChangeLog %doc aprx.conf aprx-complex.conf %doc doc/aprx-manual.pdf %doc ViscousDigipeater.README ViscousDigipeaterTxEffect.png %dir /var/log/aprx %if 0%{?rhel} >= 7 || 0%{?fedora} >= 16 %{_unitdir}/%{name}.service %else %{_initddir}/%{name} %endif %config(noreplace) %{_sysconfdir}/aprx.conf %config(noreplace) %{_sysconfdir}/sysconfig/aprx %config(noreplace) %{_sysconfdir}/logrotate.d/aprx %{_sbindir}/aprx %{_sbindir}/aprx-stat %doc %{_mandir}/man8/aprx.8.gz %doc %{_mandir}/man8/aprx-stat.8.gz %changelog * Thu Oct 11 2012 Andrew Elwell - 2.08.svn593 - Packaging for Fedora * Sat Jan 12 2008 Matti Aarnio - OH2MQK - KP20NG - - RPM framework added aprx-2.08.svn593/pbuf.h0000644000175000017500000001101712032325374013563 0ustar colincolin/* * aprsc * * (c) Heikki Hannikainen, OH7LZB * * This program is licensed under the BSD license, which can be found * in the file LICENSE. * */ /* Modified for APRX by Matti Aarnio, OH2MQK * Altered name from worker.h to pbuf.h, and * dropped about 70% of worker.h stuff... */ #ifndef PBUF_H #define PBUF_H /* minimum and maximum length of a callsign on APRS-IS */ #define CALLSIGNLEN_MIN 3 #define CALLSIGNLEN_MAX 9 /* packet length limiters and buffer sizes */ #define PACKETLEN_MIN 10 /* minimum length for a valid APRS-IS packet: "A1A>B1B:\r\n" */ #define PACKETLEN_MAX 512 /* maximum length for a valid APRS-IS packet (incl. CRLF) */ /* * Packet length statistics: * * <= 80: about 25% * <= 90: about 36% * <= 100: about 73% * <= 110: about 89% * <= 120: about 94% * <= 130: about 97% * <= 140: about 98.7% * <= 150: about 99.4% */ #define PACKETLEN_MAX_SMALL 100 #define PACKETLEN_MAX_MEDIUM 180 /* about 99.5% are smaller than this */ #define PACKETLEN_MAX_LARGE PACKETLEN_MAX /* number of pbuf_t structures to allocate at a time */ #define PBUF_ALLOCATE_BUNCH_SMALL 2000 /* grow to 2000 in production use */ #define PBUF_ALLOCATE_BUNCH_MEDIUM 2000 /* grow to 2000 in production use */ #define PBUF_ALLOCATE_BUNCH_LARGE 50 /* grow to 50 in production use */ /* a packet buffer */ /* Type flags -- some can happen in combinations: T_CWOP + T_WX / T_CWOP + T_POSITION ... */ #define T_POSITION (1 << 0) // Packet is of position type #define T_OBJECT (1 << 1) // packet is an object #define T_ITEM (1 << 2) // packet is an item #define T_MESSAGE (1 << 3) // packet is a message #define T_NWS (1 << 4) // packet is a NWS message #define T_WX (1 << 5) // packet is WX data #define T_TELEMETRY (1 << 6) // packet is telemetry #define T_QUERY (1 << 7) // packet is a query #define T_STATUS (1 << 8) // packet is status #define T_USERDEF (1 << 9) // packet is userdefined #define T_CWOP (1 << 10) // packet is recognized as CWOP #define T_STATCAPA (1 << 11) // packet is station capability response #define T_THIRDPARTY (1 << 12) #define T_ALL (1 << 15) // set on _all_ packets #define F_DUPE (1 << 0) // Duplicate of a previously seen packet #define F_HASPOS (1 << 1) // This packet has valid parsed position #define F_HAS_TCPIP (1 << 2) // There is a TCPIP* in the path struct pbuf_t { struct pbuf_t *next; int16_t is_aprs; // If not, then just digipeated frame.. int16_t digi_like_aprs; int16_t source_if_group; int16_t refcount; int16_t reqcount; // How many digipeat hops are requested? int16_t donecount; // How many digipeat hops are already done? time_t t; /* when the packet was received */ uint32_t seqnum; /* ever increasing counter, dupecheck sets */ uint16_t packettype; /* bitmask: one or more of T_* */ uint16_t flags; /* bitmask: one or more of F_* */ uint16_t srcname_len; /* parsed length of source (object, item, srcall) name 3..9 */ uint16_t dstcall_len; /* parsed length of destination callsign *including* SSID */ uint16_t dstname_len; /* parsed length of message destination including SSID */ uint16_t entrycall_len; int packet_len; /* the actual length of the TNC2 packet */ int buf_len; /* the length of this buffer */ const char *srccall_end; /* source callsign with SSID */ const char *dstcall_end_or_ssid; /* end of dest callsign (without SSID) */ const char *dstcall_end; /* end of dest callsign with SSID */ const char *qconst_start; /* "qAX,incomingSSID:" -- for q and e filters */ const char *info_start; /* pointer to start of info field */ const char *srcname; /* source's name (either srccall or object/item name) */ const char *dstname; /* message destination callsign */ float lat; /* if the packet is PT_POSITION, latitude and longitude go here */ float lng; /* .. in RADIAN */ float cos_lat; /* cache of COS of LATitude for radial distance filter */ char symbol[3]; /* 2(+1) chars of symbol, if any, NUL for not found */ uint8_t *ax25addr; // Start of AX.25 address int ax25addrlen; // length of AX.25 address uint8_t *ax25data; // Start of AX.25 data after addresses int ax25datalen; // length of that data char data[1]; }; /* global packet buffer */ extern struct pbuf_t *pbuf_global; extern struct pbuf_t *pbuf_global_last; extern struct pbuf_t **pbuf_global_prevp; extern struct pbuf_t *pbuf_global_dupe; extern struct pbuf_t *pbuf_global_dupe_last; extern struct pbuf_t **pbuf_global_dupe_prevp; #endif aprx-2.08.svn593/keyhash.h0000644000175000017500000000143512305424764014274 0ustar colincolin/******************************************************************** * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * ********************************************************************/ #ifndef KEYHASH_H #define KEYHASH_H extern void keyhash_init(void); extern unsigned int keyhash(const void *s, int slen, unsigned int hash0); extern unsigned int keyhashuc(const void *s, int slen, unsigned int hash0); #endif aprx-2.08.svn593/filter.c0000644000175000017500000016543012305424767014131 0ustar colincolin/******************************************************************** * APRX -- 2nd generation APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * ********************************************************************/ /* * aprsc * * (c) Matti Aarnio, OH2MQK, * * This program is licensed under the BSD license, which can be found * in the file LICENSE. * */ #include "aprx.h" #include "cellmalloc.h" #include "historydb.h" #include "keyhash.h" /* See: http://www.aprs-is.net/javaprssrvr/javaprsfilter.htm a/latN/lonW/latS/lonE Area filter b/call1/call2... Budlist filter (*) d/digi1/digi2... Digipeater filter (*) e/call1/call1/... Entry station filter (*) f/call/dist Friend Range filter g/call1/call2.. Group Messaging filter (*) m/dist My Range filter o/obj1/obj2... Object filter (*) p/aa/bb/cc... Prefix filter q/con/ana q Contruct filter r/lat/lon/dist Range filter s/pri/alt/over Symbol filter t/poimntqsu3*c Type filter t/poimntqsu3*c/call/km Type filter u/unproto1/unproto2/.. Unproto filter (*) Sample usage frequencies (out of entire APRS-IS): 23.7 a/ <-- Optimize! 9.2 b/ <-- Optimize? 1.4 d/ 0.2 e/ 2.2 f/ 20.9 m/ <-- Optimize! 0.2 o/ 14.4 p/ <-- Optimize! 0.0 pk 0.0 pm 0.4 q/ 19.0 r/ <-- Optimize! 0.1 s_ 1.6 s/ 6.6 t/ 0.1 u/ (*) = wild-card supported Undocumented at above web-page, but apparent behaviour is: - Everything not explicitely stated to be case sensitive is case INSENSITIVE - Minus-prefixes on filters behave as is there are two sets of filters: - filters without minus-prefixes add on approved set, and all those without are evaluated at first - filters with minus-prefixes are evaluated afterwards to drop selections after the additive filter has been evaluated - Our current behaviour is: "evaluate everything in entry order, stop at first match", which enables filters like: p/OH2R -p/OH2 p/OH while javAPRSSrvr filter adjunct behaves like the request is: -p/OH2 p/OH that is, OH2R** stations are not passed thru. */ /* FIXME: What exactly is the meaning of negation on the pattern ? ** Match as a failure to match, and stop searching ? ** Something filter dependent ? ** ** javAPRSSrvr Filter Adjunct manual tells: #14 Exclusion filter All the above filters also support exclusion. Be prefixing the above filters with a dash the result will be the opposite. Any packet that match the exclusion filter will NOT pass. The exclusion filters will be processed first so if there is a match for an exclusion then the packet is not passed no matter any other filter definitions. */ #define WildCard 0x80 /* it is wild-carded prefix string */ #define NegationFlag 0x40 /* */ #define LengthMask 0x0F /* only low 4 bits encode length */ /* values above are chosen for 4 byte alignment.. */ struct filter_refcallsign_t { char callsign[CALLSIGNLEN_MAX+1]; /* size: 10.. */ int8_t reflen; /* length and flags */ }; struct filter_head_t { struct filter_t *next; const char *text; /* filter text as is */ float f_latN, f_lonE; union { float f_latS; /* for A filter */ float f_coslat; /* for R filter */ } u1; union { float f_lonW; /* for A filter */ float f_dist; /* for R filter */ } u2; time_t hist_age; char type; /* 1 char */ int16_t negation; /* boolean flag */ union { int16_t numnames; /* used as named, and as cache validity flag */ int16_t len1s; /* or len1 of s-filter */ } u3; union { int16_t bitflags; /* used as bit-set on T_*** enumerations */ int16_t len1; /* or as len2 of s-filter */ } u4; union { struct { int16_t len2s, len2, len3s, len3; } lens; /* of s-filter */ /* for cases where there is only one.. */ struct filter_refcallsign_t refcallsign; /* malloc()ed array, alignment important! */ struct filter_refcallsign_t *refcallsigns; } u5; }; struct filter_t { struct filter_head_t h; #define FILT_TEXTBUFSIZE (508-sizeof(struct filter_head_t)) char textbuf[FILT_TEXTBUFSIZE]; }; #define QC_C 0x001 /* Q-filter flag bits */ #define QC_X 0x002 #define QC_U 0x004 #define QC_o 0x008 #define QC_O 0x010 #define QC_S 0x020 #define QC_r 0x040 #define QC_R 0x080 #define QC_Z 0x100 #define QC_I 0x200 #define QC_AnalyticsI 0x800 /* // For q-filter analytics: entrycall igate filter database struct filter_entrycall_t { struct filter_entrycall_t *next; time_t expirytime; uint32_t hash; int len; char callsign[CALLSIGNLEN_MAX+1]; }; */ /* struct filter_wx_t { struct filter_wx_t *next; time_t expirytime; uint32_t hash; int len; char callsign[CALLSIGNLEN_MAX+1]; }; */ typedef enum { MatchExact, MatchPrefix, MatchWild } MatchEnum; /* #define FILTER_ENTRYCALL_HASHSIZE 2048 // Around 500-600 in db, this looks // for collision free result.. int filter_entrycall_maxage = 60*60; // 1 hour, default. Validity on // lookups: 5 minutes less.. int filter_entrycall_cellgauge; struct filter_entrycall_t *filter_entrycall_hash[FILTER_ENTRYCALL_HASHSIZE]; */ /* #define FILTER_WX_HASHSIZE 1024 // Around 300-400 in db, this looks // for collision free result.. int filter_wx_maxage = 60*60; // 1 hour, default. Validity on // lookups: 5 minutes less.. int filter_wx_cellgauge; struct filter_wx_t *filter_wx_hash[FILTER_WX_HASHSIZE]; */ #ifndef _FOR_VALGRIND_ cellarena_t *filter_cells; //cellarena_t *filter_entrycall_cells; //cellarena_t *filter_wx_cells; #endif int hist_lookup_interval = 20; /* FIXME: Configurable: Cache historydb position lookups this much seconds on each filter entry referring to some fixed callsign (f,m,t) */ float filter_lat2rad(float lat) { return (lat * (M_PI / 180.0)); } float filter_lon2rad(float lon) { return (lon * (M_PI / 180.0)); } const int filter_cellsize = sizeof(struct filter_t); const int filter_cellalign = __alignof__(struct filter_t); void filter_init(void) { #ifndef _FOR_VALGRIND_ /* A _few_... */ filter_cells = cellinit( "filter", filter_cellsize, filter_cellalign, CELLMALLOC_POLICY_LIFO, 4 /* 4 kB at the time, should be enough in all cases.. */, 0 /* minfree */ ); /* printf("filter: sizeof=%d alignof=%d\n", sizeof(struct filter_t),__alignof__(struct filter_t)); */ /* // Couple thousand filter_entrycall_cells = cellinit( "entrycall", sizeof(struct filter_entrycall_t), __alignof__(struct filter_entrycall_t), CELLMALLOC_POLICY_FIFO, 32, // 32 kB at the time, 0 // minfree ); */ /* // Under 1 thousand.. filter_wx_cells = cellinit( "wxcalls", sizeof(struct filter_wx_t), __alignof__(struct filter_wx_t), CELLMALLOC_POLICY_FIFO, 32, // 32 kB at the time 0 // minfree ); */ #endif } #if 0 static void filter_entrycall_free(struct filter_entrycall_t *f) { #ifndef _FOR_VALGRIND_ cellfree( filter_entrycall_cells, f ); #else free(f); #endif -- filter_entrycall_cellgauge; } /* * filter_entrycall_insert() is for support of q//i filters. * That is, "pass on any message that has traversed thru entry * igate which has identified itself with qAr or qAR. Not all * messages traversed thru such gate will have those same q-cons * values, thus this database keeps info about entry igate that * have shown such capability in recent past. * * This must be called by the incoming_parse() in every case * (or at least when qcons is either 'r' or 'R'.) * * The key has no guaranteed alignment, no way to play tricks * with gcc builtin optimizers. */ int filter_entrycall_insert(struct pbuf_t *pb) { struct filter_entrycall_t *f, **fp, *f2; /* OK, pre-parsing produced accepted result */ uint32_t hash; int idx, keylen; const char qcons = pb->qconst_start[2]; const char *key = pb->qconst_start+4; char uckey[CALLSIGNLEN_MAX+1]; for (keylen = 0; keylen < CALLSIGNLEN_MAX; ++keylen) { int c = key[keylen]; if (c == ',' || c == ':') break; if ('a' <= c && c <= 'z') c -= ('a' - 'A'); uckey[keylen] = c; uckey[keylen+1] = 0; } if ((key[keylen] != ',' && key[keylen] != ':') || keylen < CALLSIGNLEN_MIN) return 0; /* Bad entry-station callsign */ pb->entrycall_len = keylen; // FIXME: should be in incoming parser... /* We insert only those that have Q-Constructs of qAR or qAr */ if (qcons != 'r' && qcons != 'R') return 0; hash = keyhash(uckey, keylen, 0); idx = (hash ^ (hash >> 11) ^ (hash >> 22) ) % FILTER_ENTRYCALL_HASHSIZE; /* Fold the hashbits.. */ fp = &filter_entrycall_hash[idx]; f2 = NULL; while (( f = *fp )) { if ( f->hash == hash ) { if (f->len == keylen) { int cmp = strncasecmp(f->callsign, uckey, keylen); if (cmp == 0) { /* Have key match */ f->expirytime = tick.tv_sec + filter_entrycall_maxage; f2 = f; break; } } } /* No match at all, advance the pointer.. */ fp = &(f -> next); } if (!f2) { /* Allocate and insert into hash table */ fp = &filter_entrycall_hash[idx]; #ifndef _FOR_VALGRIND_ f = cellmalloc(filter_entrycall_cells); #else f = calloc(1, sizeof(*f)); #endif if (f) { f->next = *fp; f->expirytime = tick.tv_sec + filter_entrycall_maxage; f->hash = hash; f->len = keylen; memcpy(f->callsign, uckey, keylen); memset(f->callsign+keylen, 0, sizeof(f->callsign)-keylen); *fp = f2 = f; ++ filter_entrycall_cellgauge; } } return (f2 != NULL); } /* * filter_entrycall_lookup() is for support of q//i filters. * That is, "pass on any message that has traversed thru entry * igate which has identified itself with qAr or qAR. Not all * messages traversed thru such gate will have those same q-cons * values, thus this keeps database about entry servers that have * shown such capability in recent past. * * The key has no guaranteed alignment, no way to play tricks * with gcc builtin optimizers. */ static int filter_entrycall_lookup(const struct pbuf_t *pb) { struct filter_entrycall_t *f, **fp, *f2; const char *key = pb->qconst_start+4; const int keylen = pb->entrycall_len; uint32_t hash = keyhashuc(key, keylen, 0); int idx = ( hash ^ (hash >> 11) ^ (hash >> 22) ) % FILTER_ENTRYCALL_HASHSIZE; /* fold the hashbits.. */ f2 = NULL; fp = &filter_entrycall_hash[idx]; while (( f = *fp )) { if ( f->hash == hash ) { if (f->len == keylen) { int rc = strncasecmp(f->callsign, key, keylen); if (rc == 0) { /* Have key match, see if it is still valid entry ? */ if ((f->expirytime - (tick.tv_sec - 60)) < 0) { f2 = f; break; } } } } /* No match at all, advance the pointer.. */ fp = &(f -> next); } return (f2 != NULL); } /* * The filter_entrycall_cleanup() does purge old entries * out of the database. Run about once a minute. */ void filter_entrycall_cleanup(void) { int k, cleancount = 0; struct filter_entrycall_t *f, **fp; for (k = 0; k < FILTER_ENTRYCALL_HASHSIZE; ++k) { fp = & filter_entrycall_hash[k]; while (( f = *fp )) { /* Did it expire ? */ if ((f->expirytime - tick.tv_sec) <= 0) { *fp = f->next; f->next = NULL; filter_entrycall_free(f); ++cleancount; continue; } /* No purge, advance the pointer.. */ fp = &(f -> next); } } // hlog( LOG_DEBUG, "filter_entrycall_cleanup() removed %d entries, count now: %ld", // cleancount, filter_entrycall_cellgauge ); } /* * The filter_entrycall_atend() does purge all entries * out of the database. Run at the exit of the program. * This exists primarily to make valgrind happy... */ void filter_entrycall_atend(void) { int k; struct filter_entrycall_t *f, **fp; for (k = 0; k < FILTER_ENTRYCALL_HASHSIZE; ++k) { fp = & filter_entrycall_hash[k]; while (( f = *fp )) { *fp = f->next; f->next = NULL; filter_entrycall_free(f); } } } void filter_entrycall_dump(FILE *fp) { int k; struct filter_entrycall_t *f; for (k = 0; k < FILTER_ENTRYCALL_HASHSIZE; ++k) { f = filter_entrycall_hash[k]; for ( ; f; f = f->next ) { fprintf( fp, "%ld\t%s\n", (long)f->expirytime, f->callsign ); } } } #endif /* ================================================================ */ #if 0 static void filter_wx_free(struct filter_wx_t *f) { #ifndef _FOR_VALGRIND_ cellfree( filter_wx_cells, f ); #else free(f); #endif --filter_wx_cellgauge; } /* * The filter_wx_insert() does lookup key storage for problem of: * * Positionless T_WX packets want also position packets on output filters. */ int filter_wx_insert(struct pbuf_t *pb) { struct filter_wx_t *f, **fp, *f2; /* OK, pre-parsing produced accepted result */ const char *key = pb->data; const int keylen = pb->srccall_end - key; uint32_t hash; int idx; char uckey[CALLSIGNLEN_MAX+1]; /* If it is not a WX packet without position, we are not intrerested */ if (!((pb->packettype & T_WX) && !(pb->flags & F_HASPOS))) return 0; for (idx = 0; idx <= keylen && idx < CALLSIGNLEN_MAX; ++idx) { int c = key[idx]; if (c == ',' || c == ':') break; if ('a' <= c && c <= 'z') c -= ('a' - 'A'); uckey[idx] = c; uckey[idx+1] = 0; } hash = keyhash(uckey, keylen, 0); idx = ( hash ^ (hash >> 10) ^ (hash >> 20) ) % FILTER_WX_HASHSIZE; /* fold the hashbits.. */ fp = &filter_wx_hash[idx]; f2 = NULL; while (( f = *fp )) { if ( f->hash == hash ) { if (f->len == keylen) { int cmp = memcmp(f->callsign, uckey, keylen); if (cmp == 0) { /* Have key match */ f->expirytime = tick.tv_sec + filter_wx_maxage; f2 = f; break; } } } /* No match at all, advance the pointer.. */ fp = &(f -> next); } if (!f2) { /* Allocate and insert into hash table */ fp = &filter_wx_hash[idx]; #ifndef _FOR_VALGRIND_ f = cellmalloc(filter_wx_cells); #else f = calloc(1, sizeof(*f)); #endif ++filter_wx_cellgauge; if (f) { f->next = *fp; f->expirytime = tick.tv_sec + filter_wx_maxage; f->hash = hash; f->len = keylen; memcpy(f->callsign, key, keylen); memset(f->callsign+keylen, 0, sizeof(f->callsign)-keylen); *fp = f2 = f; } } return 0; } static int filter_wx_lookup(const struct pbuf_t *pb) { struct filter_wx_t *f, **fp, *f2; const char *key = pb->data; const int keylen = pb->srccall_end - key; uint32_t hash = keyhashuc(key, keylen, 0); int idx = ( hash ^ (hash >> 10) ^ (hash >> 20) ) % FILTER_WX_HASHSIZE; /* fold the hashbits.. */ f2 = NULL; fp = &filter_wx_hash[idx]; while (( f = *fp )) { if ( f->hash == hash ) { if (f->len == keylen) { int rc = strncasecmp(f->callsign, key, keylen); if (rc == 0) { /* Have key match, see if it is still valid entry ? */ if ((f->expirytime - (tick.tv_sec - 60)) < 0) { f2 = f; break; } } } } /* No match at all, advance the pointer.. */ fp = &(f -> next); } return (f2 != NULL); } /* * The filter_wx_cleanup() does purge old entries * out of the database. Run about once a minute. */ void filter_wx_cleanup(void) { int k, cleancount = 0; struct filter_wx_t *f, **fp; for (k = 0; k < FILTER_WX_HASHSIZE; ++k) { fp = & filter_wx_hash[k]; while (( f = *fp )) { /* Did it expire ? */ if ((f->expirytime - tick.tv_sec) <= 0) { *fp = f->next; f->next = NULL; filter_wx_free(f); ++cleancount; continue; } /* No purge, advance the pointer.. */ fp = &(f -> next); } } // hlog( LOG_DEBUG, "filter_wx_cleanup() removed %d entries, count now: %ld", // cleancount, filter_wx_cellgauge ); } /* * The filter_wx_atend() does purge all entries * out of the database. Run at the exit of the program. * This exists primarily to make valgrind happy... */ void filter_wx_atend(void) { int k; struct filter_wx_t *f, **fp; for (k = 0; k < FILTER_WX_HASHSIZE; ++k) { fp = & filter_wx_hash[k]; while (( f = *fp )) { *fp = f->next; f->next = NULL; filter_wx_free(f); } } } void filter_wx_dump(FILE *fp) { int k; struct filter_wx_t *f; for (k = 0; k < FILTER_WX_HASHSIZE; ++k) { f = filter_wx_hash[k]; for ( ; f; f = f->next ) { fprintf( fp, "%ld\t%s\n", (long)f->expirytime, f->callsign ); } } } #endif /* ================================================================ */ void filter_preprocess_dupefilter(struct pbuf_t *pbuf) { #if 0 filter_entrycall_insert(pbuf); filter_wx_insert(pbuf); #endif } void filter_postprocess_dupefilter(struct pbuf_t *pbuf, historydb_t *historydb) { /* * If there is no position at this packet from earlier * processing, try now to find one by the callsign of * the packet sender. * */ #ifndef DISABLE_IGATE if (!(pbuf->flags & F_HASPOS)) { history_cell_t *hist; hist = historydb_lookup(historydb, pbuf->srcname, pbuf->srcname_len); // hlog( LOG_DEBUG, "postprocess_dupefilter: no pos, looking up '%.*s', rc=%d", // pbuf->srcname_len, pbuf->srcname, rc ); if (hist != NULL) { pbuf->lat = hist->lat; pbuf->lng = hist->lon; pbuf->cos_lat = hist->coslat; pbuf->flags |= F_HASPOS; } } #endif } /* ================================================================ */ /* * filter_match_on_callsignset() matches prefixes, or exact keys * on filters of types: b, d, e, o, p, u * ('p' and 'b' need OPTIMIZATION - others get it for free) * */ static int filter_match_on_callsignset(struct filter_refcallsign_t *ref, int keylen, struct filter_t *f, const MatchEnum wildok) { int i; struct filter_refcallsign_t *r = f->h.u5.refcallsigns; const char *r1 = (const void*)ref->callsign; if (debug) printf(" filter_match_on_callsignset(ref='%s', keylen=%d, filter='%s')\n", ref->callsign, keylen, f->h.text); for (i = 0; i < f->h.u3.numnames; ++i) { const int reflen = r[i].reflen; const int len = reflen & LengthMask; const char *r2 = (const void*)r[i].callsign; if (debug)printf(" .. reflen=0x%02x r2='%s'\n", reflen & 0xFF, r2); switch (wildok) { case MatchExact: if (len != keylen) continue; /* no match */ /* length OK, compare content */ if (strncasecmp( r1, r2, len ) != 0) continue; /* So it was an exact match ** Precisely speaking.. we should check that there is ** no WildCard flag, or such. But then this match ** method should not be used if parser finds any such. */ return ( reflen & NegationFlag ? 2 : 1 ); break; case MatchPrefix: if (len > keylen || !len) { /* reference string length is longer than our key */ continue; } if (strncasecmp( r1, r2, len ) != 0) continue; return ( reflen & NegationFlag ? 2 : 1 ); break; case MatchWild: if (len > keylen || !len) { /* reference string length is longer than our key */ continue; } if (strncasecmp( r1, r2, len ) != 0) continue; if (reflen & WildCard) return ( reflen & NegationFlag ? 2 : 1 ); if (len == keylen) return ( reflen & NegationFlag ? 2 : 1 ); break; default: break; } } return 0; /* no match */ } /* * filter_parse_one_callsignset() collects multiple callsigns * on filters of types: b, d, e, o, p, u * * If previous filter was of same type as this one, that one's refbuf is extended. */ static int filter_parse_one_callsignset(struct filter_t **ffp, struct filter_t *f0, const char *filt0, MatchEnum wildok) { char prefixbuf[CALLSIGNLEN_MAX+1]; char *k; const char *p; int i, refcount, wildcard; int refmax = 0, extend = 0; struct filter_refcallsign_t *refbuf; struct filter_t *ff = *ffp; p = filt0; if (*p == '-') ++p; // Skip the first '/' while (*p && *p != '/') ++p; if (*p == '/') ++p; /* count the number of prefixes in there.. */ while (*p) { if (*p) ++refmax; while (*p && *p != '/') ++p; if (*p == '/') ++p; } if (refmax == 0) { printf("Filter definition of '%s' has no prefixes defined.\n", filt0); return -1; /* No prefixes ?? */ } if (ff && ff->h.type == f0->h.type) { /* SAME TYPE, extend previous record! */ extend = 1; refcount = ff->h.u3.numnames + refmax; refbuf = realloc(ff->h.u5.refcallsigns, sizeof(*refbuf) * refcount); ff->h.u5.refcallsigns = refbuf; refcount = ff->h.u3.numnames; } else { refbuf = calloc(1, sizeof(*refbuf)*refmax); refcount = 0; f0->h.u5.refcallsigns = refbuf; f0->h.u3.numnames = 0; } p = filt0; if (*p == '-') ++p; // Skip the first '/' while (*p && *p != '/') ++p; if (*p == '/') ++p; /* hlog(LOG_DEBUG, "p-filter: '%s' vs. '%s'", p, keybuf); */ while (*p) { k = prefixbuf; memset(prefixbuf, 0, sizeof(prefixbuf)); i = 0; wildcard = 0; while (*p != 0 && *p != '/') { int c = *p++; if (c == '*') { wildcard = 1; if (wildok != MatchWild) { printf("Wild-card matching not permitted, yet filter definition says: '%s'\n", filt0); return -1; } continue; } if (i < CALLSIGNLEN_MAX) { *k++ = c; ++i; } else { printf("Too long callsign string: '%s' input: '%s'\n", prefixbuf, filt0); return -1; // invalid input } } *k = 0; /* OK, we have one prefix part collected, scan source until next '/' */ if (*p != 0 && *p != '/') ++p; if (*p == '/') ++p; /* If there is more of patterns, the loop continues.. */ /* Store the refprefix */ memset(&refbuf[refcount], 0, sizeof(refbuf[refcount])); memcpy(refbuf[refcount].callsign, prefixbuf, sizeof(refbuf[refcount].callsign)); refbuf[refcount].reflen = strlen(prefixbuf); if (wildcard) refbuf[refcount].reflen |= WildCard; if (f0->h.negation) refbuf[refcount].reflen |= NegationFlag; ++refcount; } f0->h.u3.numnames = refcount; if (extend) { char *s; ff->h.u3.numnames = refcount; i = strlen(ff->h.text) + strlen(filt0)+2; if (i <= FILT_TEXTBUFSIZE) { /* Fits in our built-in buffer block - like previous.. ** Append on existing buffer */ s = ff->textbuf + strlen(ff->textbuf); sprintf(s, " %s", filt0); } else { /* It does not fit anymore.. */ s = malloc(i); /* alloc a new one */ sprintf(s, "%s %s", p, filt0); /* .. and catenate. */ p = ff->h.text; if (ff->h.text != ff->textbuf) /* possibly free old */ free((void*)p); ff->h.text = s; /* store new */ } } /* If not extending existing filter item, let main parser do the finalizations */ return extend; } int filter_parse_one_s(struct filter_t *f0, struct filter_t **ffp, const char *filt0) { /* s/pri/alt/over Symbol filter pri = symbols in primary table alt = symbols in alternate table over = overlay character (case sensitive) For example: s/-> This will pass all House and Car symbols (primary table) s//# This will pass all Digi with or without overlay s//#/T This will pass all Digi with overlay of capital T About 10-15 s-filters in entire APRS-IS core at any given time. Up to 520 invocations per second at peak. */ const char *s = filt0; // struct filter_t *ff = *ffp; int len1, len2, len3, len4, len5, len6; if (*s == '-') ++s; if (*s == 's' || *s == 'S') ++s; if (*s != '/') return -1; ++s; len1 = len2 = len3 = len4 = len5 = len6 = 0; while (1) { len1 = s - filt0; while (*s && *s != '/') ++s; len2 = s - filt0; f0->h.u3.len1s = len1; f0->h.u4.len1 = len2 - len1; f0->h.u5.lens.len2s = f0->h.u5.lens.len2 = f0->h.u5.lens.len3s = f0->h.u5.lens.len3 = 0; if (!*s) break; if (*s == '/') ++s; len3 = s - filt0; while (*s && *s != '/') ++s; len4 = s - filt0; f0->h.u5.lens.len2s = len3; f0->h.u5.lens.len2 = len4 - len3; if (!*s) break; if (*s == '/') ++s; len5 = s - filt0; while (*s) ++s; len6 = s - filt0; f0->h.u5.lens.len3s = len5; f0->h.u5.lens.len3 = len6 - len5; break; } if ((len6-len5 > 0) && (len4-len3 == 0)) { /* overlay but no secondary table.. */ return -1; /* bad parse */ } #if 0 { const char *s1 = filt0+len1, *s2 = filt0+len3, *s3 = filt0+len5; int l1 = len2-len1, l2 = len4-len3, l3 = len6-len5; // hlog( LOG_DEBUG, "parse s-filter: '%.*s' '%.*s' '%.*s'", l1, s1, l2, s2, l3, s3 ); } #endif return 0; } int filter_parse(struct filter_t **ffp, const char *filt) { struct filter_t f0; int i; const char *filt0 = filt; const char *s; char dummyc, dummy2; struct filter_t *ff, *f; ff = *ffp; for ( ; ff && ff->h.next; ff = ff->h.next) ; /* ff points to last so far accumulated filter, if none were previously received, it is NULL.. */ memset(&f0, 0, sizeof(f0)); if (*filt == '-') { f0.h.negation = 1; ++filt; } f0.h.type = *filt; if (!strchr("abdefmopqrstuABDEFMOPQRSTU",*filt)) { // Not valid filter code // hlog(LOG_DEBUG, "Bad filter code: %s", filt0); if (debug) printf("Bad filter code: %s\n", filt0); return -1; } switch (f0.h.type) { case 'a': case 'A': /* a/latN/lonW/latS/lonE Area filter -- OPTIMIZE! */ f0.h.type = 'a'; // inside area i = sscanf(filt+1, "/%f/%f/%f/%f%c%c", &f0.h.f_latN, &f0.h.u2.f_lonW, &f0.h.u1.f_latS, &f0.h.f_lonE, &dummyc, &dummy2); if (i == 6 && dummyc == '/' && dummy2 == '-') { i = 4; f0.h.type = 'A'; // outside area! } if (i == 5 && dummyc == '-') { i = 4; f0.h.type = 'A'; // outside area! } if (i == 5 && dummyc == '/') { i = 4; } if (i != 4) { // hlog(LOG_DEBUG, "Bad filter parse: %s", filt0); if (debug) printf("Bad filter parse: %s", filt0); return -1; } if (!( -90.01 < f0.h.f_latN && f0.h.f_latN < 90.01)) { // hlog(LOG_DEBUG, "Bad filter latN value: %s", filt0); if (debug) printf("Bad filter latN value: %s", filt0); return -2; } if (!(-180.01 < f0.h.u2.f_lonW && f0.h.u2.f_lonW < 180.01)) { // hlog(LOG_DEBUG, "Bad filter lonW value: %s", filt0); if (debug) printf("Bad filter lonW value: %s", filt0); return -2; } if (!( -90.01 < f0.h.u1.f_latS && f0.h.u1.f_latS < 90.01)) { // hlog(LOG_DEBUG, "Bad filter latS value: %s", filt0); if (debug) printf("Bad filter latS value: %s", filt0); return -2; } if (!(-180.01 < f0.h.f_lonE && f0.h.f_lonE < 180.01)) { // hlog(LOG_DEBUG, "Bad filter lonE value: %s", filt0); if (debug) printf("Bad filter lonE value: %s", filt0); return -2; } if (f0.h.u2.f_lonW > f0.h.f_lonE) { // wrong way, swap longitudes float t = f0.h.u2.f_lonW; f0.h.u2.f_lonW = f0.h.f_lonE; f0.h.f_lonE = t; } if (f0.h.u1.f_latS > f0.h.f_latN) { // wrong way, swap latitudes float t = f0.h.u1.f_latS; f0.h.u1.f_latS = f0.h.f_latN; f0.h.f_latN = t; } // hlog(LOG_DEBUG, "Filter: %s -> A %.3f %.3f %.3f %.3f", filt0, f0.h.f_latN, f0.h.f_lonW, f0.h.f_latS, f0.h.f_lonE); f0.h.f_latN = filter_lat2rad(f0.h.f_latN); f0.h.u2.f_lonW = filter_lon2rad(f0.h.u2.f_lonW); f0.h.u1.f_latS = filter_lat2rad(f0.h.u1.f_latS); f0.h.f_lonE = filter_lon2rad(f0.h.f_lonE); break; case 'b': case 'B': /* b/call1/call2... Budlist filter (*) */ i = filter_parse_one_callsignset(ffp, &f0, filt0, MatchWild ); if (i < 0) return i; if (i > 0) /* extended previous */ return 0; break; case 'd': case 'D': /* d/digi1/digi2... Digipeater filter (*) */ i = filter_parse_one_callsignset(ffp, &f0, filt0, MatchWild ); if (i < 0) return i; if (i > 0) /* extended previous */ return 0; break; #if 0 case 'e': case 'E': /* e/call1/call1/... Entry station filter (*) */ i = filter_parse_one_callsignset(ffp, &f0, filt0, MatchWild ); if (i < 0) return i; if (i > 0) /* extended previous */ return 0; break; #endif case 'f': case 'F': /* f/call/dist Friend's range filter */ i = sscanf(filt+1, "/%9[^/]/%f", f0.h.u5.refcallsign.callsign, &f0.h.u2.f_dist); // negative distance means "outside this range." // and makes most sense with overall negative filter! if (i != 2 || (-0.1 < f0.h.u2.f_dist && f0.h.u2.f_dist < 0.1)) { // hlog(LOG_DEBUG, "Bad filter parse: %s", filt0); if (debug) printf("Bad filter parse: %s", filt0); return -1; } f0.h.u5.refcallsign.callsign[CALLSIGNLEN_MAX] = 0; f0.h.u5.refcallsign.reflen = strlen(f0.h.u5.refcallsign.callsign); f0.h.u3.numnames = 0; /* reusing this as "position-cache valid" flag */ // hlog(LOG_DEBUG, "Filter: %s -> F xxx %.3f", filt0, f0.h.u2.f_dist); /* NOTE: Could do static location resolving at connect time, ** and then use the same way as 'r' range does. The friends ** are rarely moving... */ break; case 'g': case 'G': // g/call1/call2/ Group Messaging filter i = filter_parse_one_callsignset(ffp, &f0, filt0, MatchWild ); if (i < 0) return i; if (i > 0) /* extended previous */ return 0; break; case 'm': case 'M': /* m/dist My range filter */ if (myloc_latstr == NULL) { printf("The M/radius_km filter requires top-level myloc definition. It doesn't exist.\n"); return -1; } f0.h.type = 'r'; // internal implementation at Aprx is a RANGE filter. f0.h.f_latN = myloc_lat; // radians f0.h.f_lonE = myloc_lon; // radians f0.h.u1.f_coslat = myloc_coslat; i = sscanf(filt+1, "/%f", &f0.h.u2.f_dist); if (i != 1 || f0.h.u2.f_dist < 0.1) { // hlog(LOG_DEBUG, "Bad filter parse: %s", filt0); if (debug) printf("Bad filter parse: %s", filt0); return -1; } f0.h.u3.numnames = 0; /* reusing this as "position-cache valid" flag */ // hlog(LOG_DEBUG, "Filter: %s -> M %.3f", filt0, f0.h.u2.f_dist); break; case 'o': case 'O': /* o/obje1/obj2... Object filter (*) */ i = filter_parse_one_callsignset(ffp, &f0, filt0, MatchWild ); if (i < 0) return i; if (i > 0) /* extended previous */ return 0; break; case 'p': case 'P': /* p/aa/bb/cc... Prefix filter Pass traffic with fromCall that start with aa or bb or cc... */ i = filter_parse_one_callsignset(ffp, &f0, filt0, MatchWild ); if (i < 0) return i; if (i > 0) /* extended previous */ return 0; break; #if 0 case 'q': case 'Q': /* q/con/ana q Contruct filter */ s = filt+1; f0.h.type = 'q'; f0.h.u4.bitflags = 0; /* For QC_* flags */ if (*s++ != '/') { // hlog(LOG_DEBUG, "Bad q-filter parse: %s", filt0); if (debug) printf("Bad q-filter parse: %s", filt0); return -1; } for ( ; *s && *s != '/'; ++s ) { switch (*s) { case 'C': f0.h.u4.bitflags |= QC_C; break; case 'X': f0.h.u4.bitflags |= QC_X; break; case 'U': f0.h.u4.bitflags |= QC_U; break; case 'o': f0.h.u4.bitflags |= QC_o; break; case 'O': f0.h.u4.bitflags |= QC_O; break; case 'S': f0.h.u4.bitflags |= QC_S; break; case 'r': f0.h.u4.bitflags |= QC_r; break; case 'R': f0.h.u4.bitflags |= QC_R; break; case 'Z': f0.h.u4.bitflags |= QC_Z; break; case 'I': f0.h.u4.bitflags |= QC_I; break; default: // hlog(LOG_DEBUG, "Bad q-filter parse: %s", filt0); if (debug) printf("Bad q-filter parse: %s", filt0); return -1; } } if (*s == '/') { /* second format */ ++s; if (*s == 'i' || *s == 'I') { f0.h.u4.bitflags |= QC_AnalyticsI; ++s; } if (*s) { // hlog(LOG_DEBUG, "Bad q-filter parse: %s", filt0); if (debug) printf("Bad q-filter parse: %s", filt0); return -1; } } break; #endif case 'r': case 'R': /* r/lat/lon/dist Range filter */ i = sscanf(filt+1, "/%f/%f/%f", &f0.h.f_latN, &f0.h.f_lonE, &f0.h.u2.f_dist); // negative distance means "outside this range." // and makes most sense with overall negative filter! if (i != 3 || (-0.1 < f0.h.u2.f_dist && f0.h.u2.f_dist < 0.1)) { // hlog(LOG_DEBUG, "Bad filter parse: %s", filt0); if (debug) printf("Bad filter parse: %s", filt0); return -1; } if (!( -90.01 < f0.h.f_latN && f0.h.f_latN < 90.01)) { // hlog(LOG_DEBUG, "Bad filter lat value: %s", filt0); if (debug) printf("Bad filter lat value: %s", filt0); return -2; } if (!(-180.01 < f0.h.f_lonE && f0.h.f_lonE < 180.01)) { // hlog(LOG_DEBUG, "Bad filter lon value: %s", filt0); if (debug) printf("Bad filter lon value: %s", filt0); return -2; } // hlog(LOG_DEBUG, "Filter: %s -> R %.3f %.3f %.3f", filt0, f0.h.f_latN, f0.h.f_lonE, f0.h.u2.f_dist); f0.h.f_latN = filter_lat2rad(f0.h.f_latN); f0.h.f_lonE = filter_lon2rad(f0.h.f_lonE); f0.h.u1.f_coslat = cosf( f0.h.f_latN ); /* Store pre-calculated COS of LAT */ break; case 's': case 'S': /* s/pri/alt/over Symbol filter */ i = filter_parse_one_s( &f0, ffp, filt0 ); if (i < 0) { // hlog(LOG_DEBUG, "Bad s-filter syntax: %s", filt0); if (debug) printf("Bad s-filter syntax: %s", filt0); return i; } if (i > 0) /* extended previous */ return 0; break; case 't': case 'T': /* t/.............. t/............../call/km */ s = filt+1; f0.h.type = 't'; f0.h.u4.bitflags = 0; f0.h.u3.numnames = 0; /* reusing this as "position-cache valid" flag */ if (*s++ != '/') { // hlog(LOG_DEBUG, "Bad filter parse: %s", filt0); if (debug) printf("Bad t-filter syntax: %s", filt0); return -1; } for ( ; *s && *s != '/'; ++s ) { switch (*s) { case '*': f0.h.u4.bitflags |= ~T_CWOP; /* "ALL" -- excluding CWOP */ break; case '3': f0.h.u4.bitflags |= T_THIRDPARTY; break; case 'c': case 'C': f0.h.u4.bitflags |= T_CWOP; break; case 'i': case 'I': f0.h.u4.bitflags |= T_ITEM; break; case 'm': case 'M': f0.h.u4.bitflags |= T_MESSAGE; break; case 'n': case 'N': f0.h.u4.bitflags |= T_NWS; break; case 'o': case 'O': f0.h.u4.bitflags |= T_OBJECT; break; case 'p': case 'P': f0.h.u4.bitflags |= T_POSITION; break; case 'q': case 'Q': f0.h.u4.bitflags |= T_QUERY; break; case 's': case 'S': f0.h.u4.bitflags |= T_STATUS; break; case 't': case 'T': f0.h.u4.bitflags |= T_TELEMETRY; break; case 'u': case 'U': f0.h.u4.bitflags |= T_USERDEF; break; case 'w': case 'W': f0.h.u4.bitflags |= T_WX; break; default: // hlog(LOG_DEBUG, "Bad filter parse: %s", filt0); if (debug) printf("Bad t-filter syntax: %s", filt0); return -1; } } if (*s == '/' && s[1] != 0) { /* second format */ i = sscanf(s, "/%9[^/]/%f%c", f0.h.u5.refcallsign.callsign, &f0.h.u2.f_dist, &dummyc); // negative distance means "outside this range." // and makes most sense with overall negative filter! if ( i != 2 || (-0.1 < f0.h.u2.f_dist && f0.h.u2.f_dist < 0.1) || /* 0.1 km minimum radius */ strlen(f0.h.u5.refcallsign.callsign) < CALLSIGNLEN_MIN ) { // hlog(LOG_DEBUG, "Bad filter parse: %s", filt0); if (debug) printf("Bad t-filter parse: %s", filt0); return -1; } f0.h.u5.refcallsign.callsign[CALLSIGNLEN_MAX] = 0; f0.h.u5.refcallsign.reflen = strlen(f0.h.u5.refcallsign.callsign); f0.h.type = 'T'; /* two variants... */ } break; case 'u': case 'U': /* u/unproto1/unproto2... Unproto filter (*) */ i = filter_parse_one_callsignset(ffp, &f0, filt0, MatchWild ); if (i < 0) return i; if (i > 0) /* extended previous */ return 0; break; default:; /* No pre-parsers for other types */ // hlog(LOG_DEBUG, "Filter: %s", filt0); if (debug) printf("Bad filter code: %s\n", filt0); return -1; break; } // if (!c) return 0; /* Just a verification scan, not actual fill in parse */ /* OK, pre-parsing produced accepted result */ #ifndef _FOR_VALGRIND_ f = cellmalloc(filter_cells); if (!f) return -1; *f = f0; /* store pre-parsed values */ if (strlen(filt0) < FILT_TEXTBUFSIZE) { strcpy(f->textbuf, filt0); f->h.text = f->textbuf; } else f->h.text = strdup(filt0); /* and copy of filter text */ #else f = calloc(1, sizeof(*f) + strlen(filt0)); *f = f0; /* store pre-parsed values */ f->h.text = f->textbuf; strcpy(f->textbuf, filt); /* and copy of filter text */ #endif /* hlog(LOG_DEBUG, "parsed filter: t=%c n=%d '%s'", f->h.type, f->h.negation, f->h.text); */ /* link to the tail.. */ if (ff) ffp = &ff->h.next; *ffp = f; return 0; } /* Discard the defined filter chain */ void filter_free(struct filter_t *f) { struct filter_t *fnext; for ( ; f ; f = fnext ) { fnext = f->h.next; /* If not pointer to internal string, free it.. */ #ifndef _FOR_VALGRIND_ if (f->h.text != f->textbuf) free((void*)(f->h.text)); cellfree(filter_cells, f); #else free(f); #endif } } /* # # Input: This[La] Source Latitude, in radians # This[Lo] Source Longitude, in radians # That[La] Destination Latitude, in radians # That[Lo] Destination Longitude, in radians # Output: R[s] Distance, in kilometers # function maidenhead_km_distance($This, $That) { #Haversine Formula (from R.W. Sinnott, "Virtues of the Haversine", #Sky and Telescope, vol. 68, no. 2, 1984, p. 159): $dlon = $That[Lo] - $This[Lo]; $dlat = $That[La] - $This[La]; $sinDlat2 = sin($dlat/2); $sinDlon2 = sin($dlon/2); $a = ($sinDlat2 * $sinDlat2 + cos($This[La]) * cos($That[La]) * $sinDlon2 * $sinDlon2); # The Haversine Formula can be expressed in terms of a two-argument # inverse tangent function, atan2(y,x), instead of an inverse sine # as follows (no bulletproofing is needed for an inverse tangent): $c = 2.0 * atan2( sqrt($a), sqrt(1.0-$a) ); # $d = R * $c ; # Radius of ball times angle [radians] ... $R[s] = rad2deg($c) * 111.2; return($R); } */ static float maidenhead_km_distance(float lat1, float coslat1, float lon1, float lat2, float coslat2, float lon2) { float sindlat2 = sinf((lat1 - lat2) * 0.5); float sindlon2 = sinf((lon1 - lon2) * 0.5); float a = (sindlat2 * sindlat2 + coslat1 * coslat2 * sindlon2 * sindlon2); float c = 2.0 * atan2f( sqrtf(a), sqrtf(1.0 - a)); return ((111.2 * 180.0 / M_PI) * c); } /* * * http://www.aprs-is.net/javaprssrvr/javaprsfilter.htm * */ static int filter_process_one_a(struct pbuf_t *pb, struct filter_t *f) { /* a/latN/lonW/latS/lonE Area filter The area filter works the same as range filter but the filter is defined as a box of coordinates. The coordinates can also been seen as upper left coordinate and lower right. Lat/lon are decimal degrees. South and west are negative. Multiple area filters can be defined at the same time. Messages addressed to stations within the area are also passed. (by means of aprs packet parse finding out the location..) 50-70 instances in APRS-IS core at any given time. Up to 2500 invocations per second. */ ; if (!(pb->flags & F_HASPOS)) /* packet with a position.. (msgs with RECEIVER's position) */ return 0; if ((pb->lat <= f->h.f_latN) && (pb->lat >= f->h.u1.f_latS) && (pb->lng <= f->h.f_lonE) && /* East POSITIVE ! */ (pb->lng >= f->h.u2.f_lonW)) { /* Inside the box */ return f->h.negation ? 2 : 1; } else if (f->h.type == 'A') { /* Outside the box */ return f->h.negation ? 2 : 1; } return 0; } static int filter_process_one_b(struct pbuf_t *pb, struct filter_t *f) { /* b/call1/call2... Budlist filter Pass all traffic FROM exact call: call1, call2, ... (* wild card allowed) 50/70 instances in APRS-IS core at any given time. Up to 2500 invocations per second. */ struct filter_refcallsign_t ref; int i = pb->srccall_end - pb->data; if (i > CALLSIGNLEN_MAX) i = CALLSIGNLEN_MAX; /* source address "addr">... */ memset( &ref, 0, sizeof(ref) ); // clear it all memcpy( ref.callsign, pb->data, i); return filter_match_on_callsignset(&ref, i, f, MatchWild); } static int filter_process_one_d(struct pbuf_t *pb, struct filter_t *f) { /* d/digi1/digi2... Digipeater filter The digipeater filter will match all packets that have been digipeated by a particular station(s) (the station's call is in the path). This filter allows the * wildcard. 25-35 instances in APRS-IS core at any given time. Up to 1300 invocations per second. */ struct filter_refcallsign_t ref; const char *d = pb->srccall_end + 1 + pb->dstcall_len + 1; /* viacall start */ const char *q = pb->qconst_start-1; int rc, i, j = 0; // hlog( LOG_INFO, "digifilter: '%.*s' -> '%.*s' q-d=%d", // (int)(pb->packet_len < 50 ? pb->packet_len : 50), // pb->data, (int)i, d, (int)(q-d) ); for (i = 0; d < q; ) { ++j; if (j > 10) break; // way too many callsigns... (code bug?) if (*d == ',') ++d; // second round and onwards.. for (i = 0; i+d <= q && i <= CALLSIGNLEN_MAX; ++i) { if (d[i] == ',') break; } // hlog(LOG_INFO, "d: -> (%d,%d) '%.*s'", (int)(d-pb->data), i, i, d); // digipeater address ",addr," memset( &ref, 0, sizeof(ref) ); // clear it all memcpy( ref.callsign, d, i); if (i > CALLSIGNLEN_MAX) i = CALLSIGNLEN_MAX; rc = filter_match_on_callsignset(&ref, i, f, MatchWild); if (rc) { return (rc == 1); } d += i; } return 0; } #if 0 static int filter_process_one_e(struct pbuf_t *pb, struct filter_t *f) { /* e/call1/call1/... Entry station filter This filter matches all packets with the specified callsign-SSID(s) immediately following the q construct. This allows filtering based on receiving IGate, etc. Supports * wildcard. 2-6 instances in APRS-IS core at any given time. Up to 200 invocations per second. */ struct filter_refcallsign_t ref; const char *e = pb->qconst_start+4; int i = pb->entrycall_len; if (i < 1) /* should not happen.. */ return 0; /* Bad Entry-station callsign */ /* entry station address "qA*,addr," */ memset( &ref, 0, sizeof(ref) ); // clear it all memcpy( ref.callsign, e, i); return filter_match_on_callsignset(&ref, i, f, MatchWild); } #endif #ifndef DISABLE_IGATE static int filter_process_one_f(struct pbuf_t *pb, struct filter_t *f, historydb_t *historydb) { /* f/call/dist Friend Range filter This is the same as the range filter except that the center is defined as the last known position of call. Multiple friend filters can be defined at the same time. Messages addressed to stations within the range are also passed. (by means of aprs packet parse finding out the location..) NOTE: Could do static location resolving at connect time, and then use the same way as 'r' range does. The friends are rarely moving... 15-25 instances in APRS-IS core at any given time. Up to 900 invocations per second. Caching the historydb_lookup() result will lower CPU power spent on the historydb. */ history_cell_t *history; float r; float lat1, lon1, coslat1; float lat2, lon2, coslat2; const char *callsign = f->h.u5.refcallsign.callsign; int i = f->h.u5.refcallsign.reflen; if (!(pb->flags & F_HASPOS)) { /* packet with a position.. (msgs with RECEIVER's position) */ if (debug) printf("f-filter: no position -> return 0\n"); return 0; /* No position data... */ } /* find friend's last location packet */ if (f->h.hist_age < tick.tv_sec) { history = historydb_lookup( historydb, callsign, i ); f->h.hist_age = tick.tv_sec + hist_lookup_interval; if (!history) { if (debug) printf("f-filter: no history lookup result (%*s) -> return 0\n", i, callsign ); return 0; /* no lookup result.. */ } f->h.u3.numnames = 1; f->h.f_latN = history->lat; f->h.f_lonE = history->lon; f->h.u1.f_coslat = history->coslat; } if (!f->h.u3.numnames) { if (debug) printf("f-filter: no history lookup result (numnames == 0) -> return 0\n"); return 0; /* histdb lookup cache invalid */ } lat1 = f->h.f_latN; lon1 = f->h.f_lonE; coslat1 = f->h.u1.f_coslat; lat2 = pb->lat; lon2 = pb->lng; coslat2 = pb->cos_lat; r = maidenhead_km_distance(lat1, coslat1, lon1, lat2, coslat2, lon2); if (debug) printf("f-filter: r=%.1f km\n", r); if (f->h.u2.f_dist < 0.0) { // Test for _outside_ the range if (r > -f->h.u2.f_dist) /* Range is more than given limit */ return (f->h.negation) ? 2 : 1; } else { // Test for _inside_ the range if (r < f->h.u2.f_dist) /* Range is less than given limit */ return (f->h.negation) ? 2 : 1; } return 0; } #endif static int filter_process_one_g(struct pbuf_t *pb, struct filter_t *f) { /* g/call1/call2... Group Messaging filter Pass all message traffic TO calls call1/call2/... (* wild card allowed) */ struct filter_refcallsign_t ref; int i = pb->dstname_len; if (i > CALLSIGNLEN_MAX) i = CALLSIGNLEN_MAX; /* source address "addr">... */ memset( &ref, 0, sizeof(ref) ); // clear it all memcpy( ref.callsign, pb->dstname, i); return filter_match_on_callsignset(&ref, i, f, MatchWild); } #if 0 // No M filter implementation, but there is M filter parse producing R filter.. static int filter_process_one_m(struct pbuf_t *pb, struct filter_t *f) { /* m/dist My Range filter This is the same as the range filter except that the center is defined as the last known position of the logged in client. Messages addressed to stations within the range are also passed. (by means of aprs packet parse finding out the location..) NOTE: MY RANGE is rarely moving, once there is a positional fix, it could stay fixed... 80-120 instances in APRS-IS core at any given time. Up to 4200 invocations per second. Caching the historydb_lookup() result will lower CPU power spent on the historydb. At Aprx: Implemented using Range filter, and prepared at parse time.. */ float lat2, lon2, coslat2; float r; if (!(pb->flags & F_HASPOS)) /* packet with a position.. (msgs with RECEIVER's position) */ return 0; lat2 = pb->lat; lon2 = pb->lng; coslat2 = pb->cos_lat; r = maidenhead_km_distance(myloc_lat, myloc_coslat, myloc_lon, lat2, coslat2, lon2); if (f->h.u2.f_dist < 0.0) { // Test for _outside_ the range if (r > -f->h.u2.f_dist) /* Range is more than given limit */ return (f->h.negation) ? 2 : 1; } else { // Test for _inside_ the range if (r < f->h.u2.f_dist) /* Range is less than given limit */ return (f->h.negation) ? 2 : 1; } return 0; } #endif static int filter_process_one_o(struct pbuf_t *pb, struct filter_t *f) { /* o/obj1/obj2... Object filter Pass all objects with the exact name of obj1, obj2, ... (* wild card allowed) PROBABLY ALSO ITEMs Usage frequency: 0.2% .. 2 cases in entire APRS-IS core at any time. About 50-70 invocations per second at peak. */ struct filter_refcallsign_t ref; int i; // const char *s; if ( (pb->packettype & (T_OBJECT|T_ITEM)) == 0 ) { /* not an Object NOR Item */ if (debug) printf("o-filter: packet type not OBJECT nor ITEM\n"); return 0; } /* parse_aprs() has picked item/object name pointer and length.. */ // s = pb->srcname; i = pb->srcname_len; if (i < 1) { if (debug) printf("o-filter: object/item name length < 1 at the packet\n"); return 0; /* Bad object/item name */ } /* object name */ memset( &ref, 0, sizeof(ref) ); // clear it all memcpy( ref.callsign, pb->srcname, i); // copy the interesting part return filter_match_on_callsignset(&ref, i, f, MatchWild); } static int filter_process_one_p(struct pbuf_t *pb, struct filter_t *f) { /* p/aa/bb/cc... Prefix filter Pass traffic with fromCall that start with aa or bb or cc... Usage frequency: 14.4% .. 80-100 cases in entire APRS-IS core at any time. Up to 3500 invocations per second at peak. */ struct filter_refcallsign_t ref; int i = pb->srccall_end - pb->data; if (i > CALLSIGNLEN_MAX) i = CALLSIGNLEN_MAX; /* source address "addr">... */ memset( &ref, 0, sizeof(ref) ); // clear it all memcpy( ref.callsign, pb->data, i); return filter_match_on_callsignset(&ref, i, f, MatchPrefix); } #if 0 static int filter_process_one_q(struct pbuf_t *pb, struct filter_t *f) { /* q/con/ana q Contruct filter q = q Construct command con = list of q Construct to pass (case sensitive) ana = analysis based on q Construct. I = Pass positions from IGATES identified by qAr or qAR. For example: q/C Pass all traffic with qAC q/rR Pass all traffic with qAr or qAR q//I Pass all position packets from IGATES identified in other packets by qAr or qAR Usage frequency: 0.4% .. 2-6 cases in entire APRS-IS core at any time. Up to 200 invocations per second at peak. */ const char *e = pb->qconst_start+2; int mask; switch (*e) { case 'C': mask = QC_C; break; case 'X': mask = QC_X; break; case 'U': mask = QC_U; break; case 'o': mask = QC_o; break; case 'O': mask = QC_O; break; case 'S': mask = QC_S; break; case 'r': mask = QC_r; break; case 'R': mask = QC_R; break; case 'Z': mask = QC_Z; break; case 'I': mask = QC_I; break; default: return 0; /* Should not happen... */ break; } if (f->h.u4.bitflags & mask) { /* Something matched! */ return 1; } if (f->h.u4.bitflags & QC_AnalyticsI) { /* Oh ? Analytical! Has it ever been accepted into entry-igate database ? */ if (filter_entrycall_lookup(pb)) return 1; /* Found on entry-igate database! */ } return 0; /* No match */ } #endif static int filter_process_one_r(struct pbuf_t *pb, struct filter_t *f) { /* r/lat/lon/dist Range filter Pass posits and objects within dist km from lat/lon. lat and lon are signed degrees, i.e. negative for West/South and positive for East/North. Multiple range filters can be defined at the same time. Messages addressed to stations within the range are also passed. (by means of aprs packet parse finding out the location..) About 120-150 r-filters in entire APRS-IS core at any given time. Up to 5200 invocations per second at peak. */ float lat1 = f->h.f_latN; float lon1 = f->h.f_lonE; float coslat1 = f->h.u1.f_coslat; float r; float lat2, lon2, coslat2; if (!(pb->flags & F_HASPOS)) { /* packet with a position.. (msgs with RECEIVER's position) */ return 0; } lat2 = pb->lat; lon2 = pb->lng; coslat2 = pb->cos_lat; r = maidenhead_km_distance(lat1, coslat1, lon1, lat2, coslat2, lon2); if (f->h.u2.f_dist < 0.0) { // Test for _outside_ the range if (r > -f->h.u2.f_dist) /* Range is more than given limit */ return (f->h.negation) ? 2 : 1; } else { // Test for _inside_ the range if (r < f->h.u2.f_dist) /* Range is less than given limit */ return (f->h.negation) ? 2 : 1; } return 0; } static int filter_process_one_s(struct pbuf_t *pb, struct filter_t *f) { /* s/pri/alt/over Symbol filter pri = symbols in primary table alt = symbols in alternate table over = overlay character (case sensitive) For example: s/-> This will pass all House and Car symbols (primary table) s//# This will pass all Digi with or without overlay s//#/T This will pass all Digi with overlay of capital T About 10-15 s-filters in entire APRS-IS core at any given time. Up to 520 invocations per second at peak. */ const char symtable = (pb->symbol[0] == '/') ? '/' : '\\'; const char symcode = pb->symbol[1]; const char symolay = (pb->symbol[0] != symtable) ? pb->symbol[0] : 0; // hlog( LOG_DEBUG, "s-filt %c|%c|%c %s", symtable, symcode, symolay ? symolay : '-', f->h.text ); if (f->h.u4.len1 != 0) { /* Primary table symbols */ if ( symtable == '/' && memchr(f->h.text+f->h.u3.len1s, symcode, f->h.u4.len1) != NULL ) return f->h.negation ? 2 : 1; // return 0; } if (f->h.u5.lens.len3 != 0) { /* Secondary table with overlay */ if ( memchr(f->h.text+f->h.u5.lens.len3s, symolay, f->h.u5.lens.len3) == NULL ) return 0; // No match on overlay if ( memchr(f->h.text+f->h.u5.lens.len2s, symcode, f->h.u5.lens.len2) == NULL ) return 0; // No match on overlay return f->h.negation ? 2 : 1; } /* OK, no overlay... */ if (f->h.u5.lens.len2 != 0) { /* Secondary table symbols */ if ( symtable != '/' && memchr(f->h.text+f->h.u5.lens.len2s, symcode, f->h.u5.lens.len2) != NULL ) return f->h.negation ? 2 : 1; } /* No match */ return 0; } static int filter_process_one_t(struct pbuf_t *pb, struct filter_t *f, historydb_t *historydb) { /* [-]t/poimntqsu3*c [-]t/poimntqsu3*c/call/km Type filter Pass all traffic based on packet type. One or more types can be defined at the same time, t/otq is a valid definition. c = CWOP (local extension) * = ALL (local extension) i = Items m = Message n = NWS Weather & Weather Objects o = Objects p = Position packets q = Query s = Status t = Telemetry u = User-defined w = Weather 3 = 3rd party frame Note: The weather type filter also passes positions packets for positionless weather packets. The second format allows putting a radius limit around "call" (station callsign-SSID or object name) for the requested station types. About 40-60 s-filters in entire APRS-IS core at any given time. Up to 2100 invocations per second at peak. For the second format perhaps 2-3 in APRS-IS at any time. (mapping to 60-100 invocations per second) Usage examples: -t/c Everything except CWOP t/.*./OH2RDY/50 Everything within 50 km of OH2RDY's last known position ("." is dummy addition for C comments..) */ int rc = 0; if (pb->packettype & f->h.u4.bitflags) /* u4.bitflags as comparison bitmask */ rc = 1; #if 0 if (!rc && (f->h.u4.bitflags & T_WX) && (pb->flags & F_HASPOS)) { /* "Note: The weather type filter also passes positions packets // for positionless weather packets." // // 1) recognize positionless weather packets // 2) register their source callsigns, do this in input_parse // 3) when filtering for weather data, check non-weather // recognized packets against the database in point 2 // 4) pass on packets matching point 3 */ rc = filter_wx_lookup(pb); } #endif /* Either it stops here, or it continues... */ if (rc && f->h.type == 'T') { /* Within a range of callsign ? * Rather rare.. perhaps 2-3 in APRS-IS. */ float range, r; float lat1, lon1, coslat1; float lat2, lon2, coslat2; #ifndef DISABLE_IGATE const char *callsign = f->h.u5.refcallsign.callsign; const int callsignlen = f->h.u5.refcallsign.reflen; history_cell_t *history; #endif /* hlog(LOG_DEBUG, "Type filter with callsign range used! '%s'", f->h.text); */ if (!(pb->flags & F_HASPOS)) /* packet with a position.. (msgs with RECEIVER's position) */ return 0; /* No positional data.. */ range = f->h.u2.f_dist; /* So.. Now we have a callsign, and we have range. Lets find callsign's location, and range to that item.. .. 60-100 lookups per second. */ #ifndef DISABLE_IGATE if (f->h.hist_age < tick.tv_sec) { history = historydb_lookup( historydb, callsign, callsignlen ); /* hlog( LOG_DEBUG, "Type filter with callsign range used! call='%s', range=%.1f position %sfound", // callsign, range, i ? "" : "not "); */ if (!history) return 0; /* no lookup result.. */ f->h.u3.numnames = 1; f->h.hist_age = tick.tv_sec + hist_lookup_interval; f->h.f_latN = history->lat; f->h.f_lonE = history->lon; f->h.u1.f_coslat = history->coslat; } #endif if (!f->h.u3.numnames) return 0; /* No valid data at range center position cache */ lat1 = f->h.f_latN; lon1 = f->h.f_lonE; coslat1 = f->h.u1.f_coslat; lat2 = pb->lat; lon2 = pb->lng; coslat2 = pb->cos_lat; r = maidenhead_km_distance(lat1, coslat1, lon1, lat2, coslat2, lon2); if (range < 0.0) { // Test for _outside_ the range if (r > -range) /* Range is more than given limit */ return (f->h.negation) ? 2 : 1; } else { // Test for _inside_ the range if (r < range) /* Range is less than given limit */ return (f->h.negation) ? 2 : 1; } return 0; /* unimplemented! */ } return (f->h.negation ? (rc+rc) : rc); } static int filter_process_one_u(struct pbuf_t *pb, struct filter_t *f) { /* u/unproto1/unproto2/... Unproto filter This filter passes all packets with the specified destination callsign-SSID(s) (also known as the To call or unproto call). Supports * wild card. Seen hardly ever in APRS-IS core, some rare instances in Tier-2. */ struct filter_refcallsign_t ref; const char *d = pb->srccall_end+1; int i; i = pb->dstcall_len; if (i > CALLSIGNLEN_MAX) i = CALLSIGNLEN_MAX; /* hlog( LOG_INFO, "unproto: '%.*s' -> '%.*s'", // (int)(pb->packet_len < 30 ? pb->packet_len : 30), pb->data, (int)i, d); */ /* destination address ">addr," */ memset( &ref, 0, sizeof(ref) ); // clear it all memcpy( ref.callsign, d, i); return filter_match_on_callsignset(&ref, i, f, MatchWild); } static int filter_process_one(struct pbuf_t *pb, struct filter_t *f, historydb_t *historydb) { int rc = 0; if (debug>1) printf("filter_process_one() type=%c '%s'\n",f->h.type, f->h.text); switch (f->h.type) { case 'a': case 'A': rc = filter_process_one_a(pb, f); break; case 'b': case 'B': rc = filter_process_one_b(pb, f); break; case 'd': case 'D': rc = filter_process_one_d(pb, f); break; #if 0 case 'e': case 'E': rc = filter_process_one_e(pb, f); break; #endif #ifndef DISABLE_IGATE case 'f': case 'F': rc = filter_process_one_f(pb, f, historydb); break; #endif case 'g': case 'G': rc = filter_process_one_g(pb, f); break; #if 0 // these are compiled as R filters, no M filters exist internally case 'm': case 'M': rc = filter_process_one_m(pb, f); break; #endif case 'o': case 'O': rc = filter_process_one_o(pb, f); break; case 'p': case 'P': rc = filter_process_one_p(pb, f); break; #if 0 case 'q': case 'Q': rc = filter_process_one_q(pb, f); break; #endif case 'r': case 'R': rc = filter_process_one_r(pb, f); break; case 's': case 'S': rc = filter_process_one_s(pb, f); break; case 't': case 'T': rc = filter_process_one_t(pb, f, historydb); break; case 'u': case 'U': rc = filter_process_one_u(pb, f); break; default: rc = -1; break; } // hlog(LOG_DEBUG, "filter '%s' rc=%d", f->h.text, rc); return rc; } int filter_process(struct pbuf_t *pb, struct filter_t *f, historydb_t *historydb) { int seen_accept = 0; for ( ; f; f = f->h.next ) { int rc = filter_process_one(pb, f, historydb); /* no reports to user about bad filters.. */ if (rc == 1) seen_accept = 1; else if (rc == 2) return -1; /* "2" reply means: "match, but don't pass.." */ } return seen_accept; } aprx-2.08.svn593/netax25.c0000644000175000017500000005173312306650223014117 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * NETAX25: Listen on (Linux) AX.25 socket and pick all AX.25 * * data packets ... actually don't pick those * * that are going outwards. All incoming ones do pick. * * * * **************************************************************** */ #include "aprx.h" #ifdef PF_AX25 /* PF_AX25 exists -- highly likely a Linux system ! */ #include #include #include #include #include #include /* * Link-level device access * * s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_AX25)); * */ /* struct sockaddr_ll { unsigned short int sll_family; unsigned short int sll_protocol; int sll_ifindex; unsigned short int sll_hatype; unsigned char sll_pkttype; unsigned char sll_halen; unsigned char sll_addr[8]; }; SOCK_RAW Sending uses sll_ifindex and sll_protocol */ struct netax25_dev { int ifindex; int16_t protocol; uint8_t ax25addr[7]; uint8_t rxok; //uint8_t txok; uint8_t scan; char devname[IFNAMSIZ]; char callsign[10]; const struct aprx_interface *interface; }; static struct netax25_dev **netax25_devs; static int netax25_devcount; /* * Talking to Linux kernel 2.6.x, using SMACK type frames * on each configured serial port callsign -> ptymux * writer channel. If system does not write correct SMACK * frame on that KISS port for any number of reasons, * including writing incompletely buffered data, then * kernel will be able to notice that frame it received * is not valid, and discards it. (Maybe... P = 2^-16 to * accepting of error frame in spite of these controls.) */ struct netax25_pty { int fd; int ifindex; const char *callsign; const struct aprx_interface *interface; struct sockaddr_ax25 ax25addr; }; static int rx_socket = -1; static int tx_socket = -1; static struct netax25_pty **ax25rxports; static int ax25rxportscount; static char **ax25ttyports; static int *ax25ttyfds; static int ax25ttyportscount; #if defined(HAVE_OPENPTY) #ifdef HAVE_PTY_H #include #endif static void netax25_addttyport(const char *callsign, const int masterfd, const int slavefd); static const void* netax25_openpty(const char *mycall) { int rc; int disc; struct termios tio; char devname[64]; uint8_t ax25call[64]; // overlarge for AX.25 - which needs only 7 bytes, but valgrind whines.. struct ifreq ifr; int fd = -1; struct netax25_pty *nax25 = NULL; int pty_master, pty_slave; if (!mycall) return NULL; /* No mycall, no ptys! */ memset(ax25call, 0, sizeof(ax25call)); // valgrind if (parse_ax25addr(ax25call, mycall, 0x60)) { // Not valid per AX.25 rules if (debug) printf(" netax25_openpty('%s') failed to parse the parameter string as valid AX.25 callsign. Not opening kernel pty.\n", mycall); return NULL; } memset(devname, 0, sizeof(devname)); // valgrind rc = openpty(&pty_master, &pty_slave, devname, NULL, NULL); if (debug) printf("openpty() rc=%d name='%s' master=%d slave=%d\n", rc, devname, pty_master, pty_slave); if (rc != 0 || pty_slave < 0) { error_exit:; if (pty_master >= 0) close(pty_master); pty_master = -1; if (pty_slave >= 0) close(pty_slave); pty_slave = -1; if (fd >= 0) close(fd); if (debug) printf("netax25_openpty() error exit.\n"); if (nax25 != NULL) free(nax25); return NULL; /* D'uh.. */ } nax25 = calloc( 1,sizeof(*nax25) ); nax25->fd = pty_master; nax25->ifindex = -1; nax25->callsign = mycall; nax25->ax25addr.sax25_family = PF_AX25; nax25->ax25addr.sax25_ndigis = 0; memcpy(&nax25->ax25addr.sax25_call, ax25call, 7); /* setup termios parameters for this line.. */ memset(&tio, 0, sizeof(tio)); // please valgrind aprx_cfmakeraw(&tio, 0); tio.c_cc[VMIN] = 1; /* pick at least one char .. */ tio.c_cc[VTIME] = 3; /* 0.3 seconds timeout - 36 chars @ 1200 baud */ tio.c_cflag |= (CREAD | CLOCAL); cfsetispeed(&tio, B38400); /* Pseudo-tty -- pseudo speed */ cfsetospeed(&tio, B38400); rc = tcsetattr(pty_slave, TCSANOW, &tio); if (rc < 0) goto error_exit; /* The pty_slave will get N_AX25 discipline attached on itself.. */ disc = N_AX25; rc = ioctl(pty_slave, TIOCSETD, &disc); if (rc < 0) goto error_exit; rc = ioctl(pty_slave, SIOCGIFNAME, devname); if (rc < 0) goto error_exit; /* Convert mycall[] to AX.25 format callsign */ rc = ioctl(pty_slave, SIOCSIFHWADDR, ax25call); if (rc < 0) goto error_exit; /* Now set encapsulation.. */ disc = 4; rc = ioctl(pty_slave, SIOCSIFENCAP, &disc); if (rc < 0) goto error_exit; /* Then final tricks to start the interface... */ fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) goto error_exit; memset(&ifr, 0, sizeof(ifr)); // please valgrind strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)); ifr.ifr_name[sizeof(ifr.ifr_name)-1] = 0; ifr.ifr_mtu = 512; rc = ioctl(fd, SIOCSIFMTU, &ifr); if (rc < 0) goto error_exit; ifr.ifr_flags = IFF_UP | IFF_RUNNING | IFF_NOARP; rc = ioctl(fd, SIOCSIFFLAGS, &ifr); if (rc < 0) goto error_exit; close(fd); /* OK, we write and read on pty_master, the pty_slave is now attached on kernel side AX.25 interface with call: mycall */ netax25_addttyport( mycall, pty_master, pty_slave ); return (void*) nax25; } void netax25_sendax25(const void *nax25p, const void *ax25, int ax25len) { int rc, p; uint8_t ax25buf[2100]; const struct netax25_pty *nax25 = nax25p; /* kissencoder() takes AX.25 frame, and adds framing + cmd-byte */ rc = kissencoder(ax25buf, sizeof(ax25buf), LINETYPE_KISSSMACK, ax25, ax25len, 0x80); if (rc < 0) return; ax25len = rc; if (debug>2) { printf("netax25_sendax25() len=%d ",ax25len); hexdumpfp(stdout, ax25, ax25len, 1); printf("\n"); } /* Try to write it to the PTY */ p = 0; rc = write(nax25->fd, ax25buf + p, ax25len - p); if (rc < 0) rc = 0; // error hickup.. p += rc; rc = 0; if (p < ax25len) { // something left unwritten rc = write(nax25->fd, ax25buf + p, ax25len - p); if (rc < 0) rc = 0; // error hickup.. } p += rc; rc = 0; if (p < ax25len) { // something left unwritten rc = write(nax25->fd, ax25buf + p, ax25len - p); if (rc < 0) rc = 0; // error hickup.. } p += rc; rc = 0; // Now it either succeeded, or it failed. // in both cases we give up on this frame. if (p < ax25len) { if (aprxlogfile) { aprxlog("netax25_sendax25(%s,len=%d) wrote %d bytes\n", nax25->callsign, ax25len, p); } } } #else /* !HAVE_OPENPTY */ static const void* netax25_openpty(const char *mycall) { return NULL; } void netax25_sendax25(const void *nax25, const void *ax25, int ax25len) { } #endif /* HAVE_OPENPTY */ static int is_ax25ttyport(const char *callsign) { int i; for (i = 0; i < ax25ttyportscount; ++i) { if (strcmp(callsign,ax25ttyports[i]) == 0) return 1; // Have match } return 0; // No match } static int scan_linux_devices(void) { FILE *fp; struct ifreq ifr; char buffer[512], *s; int fd; struct netax25_dev ax25dev, *d; int i; // Mark all devices ready for scanning for (i = 0; i < netax25_devcount; ++i) netax25_devs[i]->scan = 0; fd = socket(PF_FILE, SOCK_DGRAM, 0); if (fd < 0) { // ... error if (debug)printf("Can not create socket(PF_FILE,SOCK_DGRAM,0); errno=%d\n", errno); return -1; } fp = fopen("/proc/net/dev", "r"); if (fp == NULL) { if (debug)printf("Can not open /proc/net/dev for reading; errno=%d\n", errno); close(fd); // ... error return -1; } // Two header lines s = fgets(buffer, sizeof(buffer), fp); s = fgets(buffer, sizeof(buffer), fp); // Then network interface names while (!feof(fp)) { if (!fgets(buffer, sizeof(buffer), fp)) break; // EOF s = strchr(buffer, ':'); if (s) *s = 0; s = buffer; while (*s == ' '||*s == '\t') ++s; memset(&ifr, 0, sizeof(ifr)); // please valgrind strncpy(ifr.ifr_name, s, IFNAMSIZ-1); ifr.ifr_name[IFNAMSIZ-1] = 0; // Is it active? if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { // error continue; } if (!(ifr.ifr_flags & IFF_UP)) continue; // not active, try next // Does it have AX.25 HW address ? if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { // Error continue; } if (ifr.ifr_hwaddr.sa_family != ARPHRD_AX25) continue; // Not AX.25 HW address, try next memset(&ax25dev, 0, sizeof(ax25dev)); memcpy(ax25dev.devname, ifr.ifr_name, IFNAMSIZ); memcpy(ax25dev.ax25addr, ifr.ifr_hwaddr.sa_data, 7); // AX.25 address ax25_to_tnc2_fmtaddress(ax25dev.callsign, ax25dev.ax25addr, 0); // in text if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { // Error continue; } ax25dev.ifindex = ifr.ifr_ifindex; // Store/Update internal kernel interface index list d = NULL; for (i = 0; i < netax25_devcount; ++i) { d = netax25_devs[i]; if (d->ifindex == ax25dev.ifindex) { d->scan = 1; // The ifindex does not change during interface lifetime break; } d = NULL; } if (d == NULL) { // Not in known interfaces, add a new one.. d = malloc(sizeof(*d)); ++netax25_devcount; netax25_devs = realloc( netax25_devs, sizeof(void*) * netax25_devcount ); netax25_devs[netax25_devcount-1] = d; memcpy(d, &ax25dev, sizeof(*d)); d->scan = 1; d->rxok = !is_ax25ttyport(d->callsign); } } fclose(fp); close(fd); // Remove devices no longer known for (i = 0; i < netax25_devcount; ++i) { if (netax25_devs[i]->scan == 0) { int j; if (debug>1)printf("Compating netax25_devs[] i=%d callsign=%s\n", i, netax25_devs[i]->callsign); free(netax25_devs[i]); for (j = i+1; j < netax25_devcount; ++j) { netax25_devs[j-1] = netax25_devs[j]; } --netax25_devcount; } } // Link interfaces for (i = 0; i < netax25_devcount; ++i) { int j; struct netax25_dev *d = netax25_devs[i]; for (j = 0; j < ax25rxportscount; ++j) { if (strcmp(ax25rxports[j]->callsign,d->callsign) == 0) { d->interface = ax25rxports[j]->interface; ax25rxports[j]->ifindex = d->ifindex; } } } return 0; } /* config interface: ax25-rxport: callsign */ void *netax25_addrxport(const char *callsign, const struct aprx_interface *interface) { struct netax25_pty *nax25p = calloc(1, sizeof(*nax25p)); nax25p->fd = -1; nax25p->interface = interface; nax25p->ax25addr.sax25_family = PF_AX25; nax25p->ax25addr.sax25_ndigis = 0; if (interface == NULL) { // Old config style if (parse_ax25addr((uint8_t*)&nax25p->ax25addr.sax25_call, callsign, 0x60)) { // Not valid per AX.25 rules free(nax25p); return NULL; } nax25p->callsign = strdup(callsign); } else { // new config fule memcpy(&nax25p->ax25addr.sax25_call, interface->ax25call, sizeof(interface->ax25call)); nax25p->callsign = interface->callsign; } ax25rxports = realloc(ax25rxports, sizeof(struct netax25_pty*) * (ax25rxportscount + 1)); ax25rxports[ax25rxportscount++] = nax25p; return nax25p; } static void netax25_addttyport(const char *callsign, const int masterfd, const int slavefd) { ax25ttyports = realloc(ax25ttyports, sizeof(void *) * (ax25ttyportscount + 1)); ax25ttyfds = realloc(ax25ttyfds, sizeof(int) * (ax25ttyportscount + 1)); ax25ttyports[ax25ttyportscount] = strdup(callsign); ax25ttyfds [ax25ttyportscount] = masterfd; /* slavefd forgotten */ ++ax25ttyportscount; } /* Nothing much in early init */ void netax25_init(void) { } /* .. but all things in late start.. */ void netax25_start(void) { int i; int rx_protocol; rx_socket = -1; /* Initialize for early bail-out */ tx_socket = -1; if (!ax25rxports) return; /* No configured receiver ports. No receiver socket creation. */ rx_protocol = ETH_P_AX25; /* Choosing ETH_P_ALL would pick also outbound packets, but also all of the ethernet traffic.. ETH_P_AX25 picks only inbound-at-ax25-devices ..packets. */ rx_socket = socket(PF_PACKET, SOCK_RAW, htons(rx_protocol)); tx_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_AX25)); if (rx_socket < 0) { i = errno; /* D'uh.. could not open it, report and leave it at that. */ fprintf(stderr, "aprx: Could not open socket(PF_PACKET,SOCK_RAW,ETH_P_AX25) for listening. Errno=%d (%s)" " -- not a big deal unless you want to receive via AX.25 sockets.\n", i, strerror(i)); return; } if (tx_socket < 0) { i = errno; /* D'uh.. could not open it, report and leave it at that. */ fprintf(stderr, "aprx: Could not open socket(PF_PACKET,SOCK_RAW,ETH_P_AX25) for sending. Errno=%d (%s)" " -- not a big deal unless you want to send via AX.25 sockets.\n", i, strerror(i)); return; } if (rx_socket >= 0) fd_nonblockingmode(rx_socket); } /* .. but all things in late start.. */ const void* netax25_open(const char *ifcallsign) { return netax25_openpty(ifcallsign); } static struct timeval next_scantime; static void netax25_resettimer(void*arg) { struct timeval *tv = (struct timeval *)arg; tv_timeradd_seconds(tv, &tick, 60); scan_linux_devices(); } int netax25_prepoll(struct aprxpolls *app) { struct pollfd *pfd; int i; if (next_scantime.tv_sec == 0) next_scantime = tick; if (time_reset) { netax25_resettimer(&next_scantime); } if (rx_socket >= 0) { /* FD is open, lets mark it for poll read.. */ pfd = aprxpolls_new(app); pfd->fd = rx_socket; pfd->events = POLLIN | POLLPRI; pfd->revents = 0; } /* read from PTY masters */ for (i = 0; i < ax25ttyportscount; ++i) { if (ax25ttyfds[i] >= 0) { pfd = aprxpolls_new(app); pfd->fd = ax25ttyfds[i]; pfd->events = POLLIN | POLLPRI; pfd->revents = 0; } } return 1; } static int rxsock_read( const int fd ) { struct sockaddr_ll sll; socklen_t sllsize; int rcvlen, ifindex, i; struct netax25_dev *netdev; uint8_t rxbuf[3000]; sllsize = sizeof(sll); rcvlen = recvfrom(fd, rxbuf, sizeof(rxbuf), 0, (struct sockaddr*)&sll, &sllsize); if (rcvlen < 0) { return 0; /* No more at this time.. */ } /* struct sockaddr_ll { unsigned short int sll_family; = PF_PACKET unsigned short int sll_protocol; = 200 ? int sll_ifindex; = 4 unsigned short int sll_hatype; = 3 = SOCK_RAW ? unsigned char sll_pkttype; = 0 unsigned char sll_halen; = 0 unsigned char sll_addr[8]; = random }; netax25rx packet len=54 from rx_socket; family=17 protocol=200 ifindex=4 hatype=3 pkttype=0 halen=0 addr=84:f9:ca:bf:d7:04:f3:b7 Data: 00 82 a0 aa 64 6a 9c e0 9e 90 70 9a b0 94 60 ae 92 88 8a 64 40 61 03 f0 3d 36 33 35 33 2e ... Text: 00 82 a0 aa d j 9c e0 9e 90 p 9a b0 94 ` ae 92 88 8a d @ a 03 f0 = 6 3 5 3 . ... AX25: 00 A P U 2 5 N p O H 8 M X J 0 W I D E 2 0 01 x 1e 1b 19 1a 19 17 ... Leads with 00 byte, then AX.25 address.. */ if (sll.sll_family != PF_PACKET || sll.sll_protocol != htons(ETH_P_AX25) || sll.sll_hatype != SOCK_RAW || sll.sll_pkttype != 0 || sll.sll_halen != 0 || rxbuf[0] != 0 ) { return 1; // Not of our interest } ifindex = sll.sll_ifindex; if (debug>1) { printf("netax25rx packet len=%d from rx_socket; family=%d protocol=%x ifindex=%d hatype=%d pkttype=%d halen=%d\n", rcvlen, sll.sll_family, sll.sll_protocol, sll.sll_ifindex, sll.sll_hatype, sll.sll_pkttype, sll.sll_halen); /* printf(" addr=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", sll.sll_addr[0],sll.sll_addr[1],sll.sll_addr[2],sll.sll_addr[3], sll.sll_addr[4],sll.sll_addr[5],sll.sll_addr[6],sll.sll_addr[7]); int i; printf("Data: "); for (i = 0; i < rcvlen; ++i) printf(" %02x", rxbuf[i]); printf("\n"); printf("Text: "); for (i = 0; i < rcvlen; ++i) { uint8_t c = rxbuf[i]; if (32 <= c && c <= 126) printf(" %c", c); else printf(" %02x", c); } printf("\n"); printf("AX25: "); for (i = 0; i < rcvlen; ++i) { uint8_t c = rxbuf[i] >> 1; if (32 <= c && c <= 126) printf(" %c", c); else printf(" %02x", c); } printf("\n"); */ } netdev = NULL; for (i = 0; i < netax25_devcount; ++i) { if (netax25_devs[i]->ifindex == ifindex) { netdev = netax25_devs[i]; break; } } if (netdev == NULL) { // Not found from Ax.25 devices if (debug>1) printf(".. not from known AX.25 device\n"); return 1; } if (netdev->interface == NULL) { if (debug>1) printf(".. not from AX.25 device configured for receiving.\n"); return 1; } if (debug) printf("Received frame of %d bytes from %s: %s\n", rcvlen, netdev->devname, netdev->callsign); // if (is_ax25ttyport(netdev->callsign)) { if (!netdev->rxok) { if (debug > 1) { printf("%s is ttyport which we serve.\n",netdev->callsign); } return 1; // We drop our own packets, if we ever see them } /// Now: actual AX.25 frame reception, // and transmit via ax25_to_tnc2() ! /* * "+10" is a magic constant for trying * to estimate channel occupation overhead */ erlang_add(netdev->callsign, ERLANG_RX, rcvlen + 10, 1); // rxsock_read() // Send it to Rx-IGate, validates also AX.25 header bits, // and returns non-zero only when things are OK for processing. // Will internally also send to interface layer, if OK. if (ax25_to_tnc2(netdev->interface, netdev->callsign, 0, rxbuf[0], rxbuf + 1, rcvlen - 1)) { // The packet is valid per AX.25 header bit rules. // ax25_to_tnc2() did send the packet to rx-igate ; } else { // The packet is not valid per AX.25 header bit rules erlang_add(netdev->callsign, ERLANG_DROP, rcvlen+10, 1); /* Account one packet */ if (aprxlogfile) { FILE *fp = fopen(aprxlogfile, "a"); if (fp) { char timebuf[60]; printtime(timebuf, sizeof(timebuf)); fprintf(fp, "%s ax25_to_tnc2(%s,len=%d) rejected the message: ", timebuf, netdev->callsign, rcvlen); hexdumpfp(fp, rxbuf, rcvlen, 1); fprintf(fp, "\n"); fclose(fp); } } } return 1; } static void discard_read_fd( const int fd ) { char buf[2000]; (void)read(fd, buf, sizeof(buf)); } int netax25_postpoll(struct aprxpolls *app) { int i, j; struct pollfd *pfd; // char ifaddress[10]; assert(app->polls != NULL); if (tv_timercmp(&tick, &next_scantime) > 0) { scan_linux_devices(); // Rescan every 60 seconds, on the dot. tv_timeradd_seconds(&next_scantime, &next_scantime, 60); } pfd = app->polls; if (rx_socket < 0) return 0; for (i = 0; i < app->pollcount; ++i, ++pfd) { if ((pfd->fd == rx_socket) && (pfd->revents & (POLLIN | POLLPRI))) { /* something coming in.. */ rxsock_read( rx_socket ); } for (j = 0; j < ax25ttyportscount; ++j) { if ((pfd->revents & (POLLIN | POLLPRI)) && (ax25ttyfds[j] == pfd->fd)) { discard_read_fd(ax25ttyfds[j]); } } } return 0; } void netax25_sendto(const void *nax25p, const uint8_t *axaddr, const int axaddrlen, const char *axdata, const int axdatalen) { const struct netax25_pty *nax25 = nax25p; struct sockaddr_ll sll; char c0[1]; struct iovec iovec[3]; struct msghdr mh; int i, len; if (tx_socket < 0) { if (debug>1) printf("netax25_sendto() tx_socket = -1, can not do..\n"); return; // D'uh.. } if (nax25->ifindex < 0) { if (debug>1) printf("netax25_sendto() ifindex < 0, can not do..\n"); return; // D'uh.. } if (debug>2) { printf("netax25_sendto() len=%d,%d ",axaddrlen,axdatalen); hexdumpfp(stdout, axaddr, axaddrlen, 1); printf(" // "); hexdumpfp(stdout, (uint8_t*)axdata, axdatalen, 0); printf("\n"); } memset(&sll, 0, sizeof(sll)); sll.sll_family = PF_PACKET; sll.sll_ifindex = nax25->ifindex; sll.sll_protocol = htons(ETH_P_AX25); sll.sll_hatype = SOCK_RAW; c0[0] = 0; iovec[0].iov_base = c0; iovec[0].iov_len = 1; iovec[1].iov_base = (void*)axaddr; // silence the compiler iovec[1].iov_len = axaddrlen; iovec[2].iov_base = (void*)axdata; // silence the compiler iovec[2].iov_len = axdatalen; len = 1+axaddrlen+axdatalen; // for debugging memset(&mh, 0, sizeof(mh)); mh.msg_name = &sll; mh.msg_namelen = sizeof(sll); mh.msg_iov = iovec; mh.msg_iovlen = 3; errno = 0; i = sendmsg(tx_socket, &mh, 0); if (debug>1)printf("netax25_sendto() the sendmsg len=%d rc=%d errno=%d\n", len, i, errno); erlang_add(nax25->callsign, ERLANG_TX, axaddrlen+axdatalen + 10, 1); // netax25_sendto() } #endif aprx-2.08.svn593/INSTALL0000644000175000017500000000250712273250104013506 0ustar colincolin INSTALL of APRX 2.08 Pre-made binary package building system exists for Debian and Redhat/Fedora systems. See details at the end of this file. A rough-cut version of the installation instructions 0) Prerequisites: Does not need anything beside standard libc! (In particular the Linux version does not need libax25 / libax25-dev !) 1) Start with ./configure --parameters For a small memory system without writable /tmp you have to use --with-embedded option. 2) Cleanliness is good start: $ make clean 3) Compile the thing: $ make 4) There is automatic "install" as: # make install with several presumptions about directories fixed into the Makefile (possibly some adjustments are required, depending upon your environment.) 5) Edit the configuration file to match your system: # emacs /etc/aprx.conf See the aprx(8) man-page for more info (man 8 aprx) 6) Program startup scripts ("init-scripts") exist for couple system environments, others may need manual adapting. For Debian users wanting to compile themselves instead of using precompiled binaries: $ make make-deb # dpkg -i aprx_2.07-....deb For RedHat/Fedora users: $ make make-rpm # rpm -Uvh aprx-2.07.svn###-1.i386.rpm aprx-2.08.svn593/aprx-stat.c0000644000175000017500000001467312305424775014570 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" int time_reset; int debug; /* linkage dummy */ int erlangout; int epochtime; const char *aprxlogfile; /* linkage dummy */ const char *mycall; /* linkage dummy */ #ifdef ERLANGSTORAGE void printtime(char *buf, int buflen) { struct tm *t = gmtime(&now.tv_sec); // strftime(timebuf, 60, "%Y-%m-%d %H:%M:%S", t); sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", t->tm_year+1900,t->tm_mon+1,t->tm_mday, t->tm_hour,t->tm_min,t->tm_sec); } void erlang_snmp(void) { int i; /* SNMP data output - continuously growing counters */ printf("APRX.pid %8ld\n", (long) ErlangHead->server_pid); printf("APRX.uptime %8ld\n", (long) (time(NULL) - ErlangHead->start_time)); printf("APRX.mycall %s\n", ErlangHead->mycall); for (i = 0; i < ErlangLinesCount; ++i) { struct erlangline *E = ErlangLines[i]; printf("%s", E->name); printf(" %ld %ld %ld %ld %ld %ld %d\n", E->SNMP.bytes_rx, E->SNMP.packets_rx, E->SNMP.bytes_rxdrop, E->SNMP.packets_rxdrop, E->SNMP.bytes_tx, E->SNMP.packets_tx, (int) (now.tv_sec - E->last_update)); } } void erlang_xml(int topmode) { int i, j, k, t; /* What this outputs is not XML, but a mild approximation of the data that XML version would output.. It is not even the whole dataset, just last 60 samples of each type. */ printf("APRX.pid %8ld\n", (long) ErlangHead->server_pid); printf("APRX.uptime %8ld\n", (long) (time(NULL) - ErlangHead->start_time)); printf("APRX.mycall %s\n", ErlangHead->mycall); for (i = 0; i < ErlangLinesCount; ++i) { struct erlangline *E = ErlangLines[i]; char logtime[40]; struct tm *wallclock; printf("\nSNMP %s", E->name); printf(" %ld %ld %ld %ld %ld %ld %d\n", E->SNMP.bytes_rx, E->SNMP.packets_rx, E->SNMP.bytes_rxdrop, E->SNMP.packets_rxdrop, E->SNMP.bytes_tx, E->SNMP.packets_tx, (int) (now.tv_sec - E->last_update)); printf("\n1min data\n"); k = E->e1_cursor; t = E->e1_max; if (topmode) t = 90; for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e1_max - 1; if (E->e1[k].update == 0) continue; if (epochtime) { sprintf(logtime, "%ld", (long) E->e1[k].update); } else { wallclock = gmtime(&E->e1[k].update); strftime(logtime, sizeof(logtime), "%Y-%m-%d %H:%M", wallclock); } printf("%s %s", logtime, E->name); printf(" %2dm %5ld %3ld %5ld %3ld %5ld %3ld %5.3f %5.3f %5.3f\n", 1, E->e1[k].bytes_rx, E->e1[k].packets_rx, E->e1[k].bytes_rxdrop, E->e1[k].packets_rxdrop, E->e1[k].bytes_tx, E->e1[k].packets_tx, (float) E->e1[k].bytes_rx / ((float) E->erlang_capa * 60.0), (float) E->e1[k].bytes_rxdrop / ((float) E->erlang_capa * 60.0), (float)E->e1[k].bytes_tx/((float)E->erlang_capa*60.0) ); } printf("\n10min data\n"); k = E->e10_cursor; t = E->e10_max; if (topmode) t = 10; for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e10_max - 1; if (E->e10[k].update == 0) continue; if (epochtime) { sprintf(logtime, "%ld", (long) E->e10[k].update); } else { wallclock = gmtime(&E->e10[k].update); strftime(logtime, sizeof(logtime), "%Y-%m-%d %H:%M", wallclock); } printf("%s %s", logtime, E->name); printf(" %2dm %5ld %3ld %5ld %3ld %5ld %3ld %5.3f %5.3f %5.3f\n", 10, E->e10[k].bytes_rx, E->e10[k].packets_rx, E->e10[k].bytes_rxdrop, E->e10[k].packets_rxdrop, E->e10[k].bytes_tx, E->e10[k].packets_tx, (float) E->e10[k].bytes_rx / ((float) E->erlang_capa * 60.0), (float) E->e10[k].bytes_rxdrop / ((float) E->erlang_capa * 60.0), (float)E->e10[k].bytes_tx/((float)E->erlang_capa*60.0) ); } printf("\n60min data\n"); k = E->e60_cursor; t = E->e60_max; if (topmode) t = 3; for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e60_max - 1; if (E->e60[k].update == 0) continue; if (epochtime) { sprintf(logtime, "%ld", (long) E->e60[k].update); } else { wallclock = gmtime(&E->e60[k].update); strftime(logtime, sizeof(logtime), "%Y-%m-%d %H:%M", wallclock); } printf("%s %s", logtime, E->name); printf(" %2dm %5ld %3ld %5ld %3ld %5ld %3ld %5.3f %5.3f %5.3f\n", 60, E->e60[k].bytes_rx, E->e60[k].packets_rx, E->e60[k].bytes_rxdrop, E->e60[k].packets_rxdrop, E->e60[k].bytes_tx, E->e60[k].packets_tx, (float) E->e60[k].bytes_rx / ((float) E->erlang_capa * 60.0), (float) E->e60[k].bytes_rxdrop / ((float) E->erlang_capa * 60.0), (float)E->e60[k].bytes_tx/((float)E->erlang_capa*60.0) ); } } exit(0); } void usage(void) { printf("Usage: aprx-stat [-t] [-f arpx-erlang.dat] {-S|-x|-X}\n"); exit(64); } int main(int argc, char **argv) { int opt; int mode_snmp = 0; int mode_xml = 0; gettimeofday(&now, NULL); while ((opt = getopt(argc, argv, "f:StxX?h")) != -1) { switch (opt) { case 'f': erlang_backingstore = optarg; break; case 'S': /* SNMP */ ++mode_snmp; break; case 'X': mode_xml = 1; break; case 'x': mode_xml = 2; break; case 't': epochtime = 1; break; default: usage(); break; } } erlang_start(0); /* Open the backing-store */ if (!ErlangHead) exit(1); if (mode_snmp) { erlang_snmp(); } else if (mode_xml == 1) { erlang_xml(0); } else if (mode_xml == 2) { erlang_xml(1); } else usage(); return 0; } #else void printtime(char *buf, int buflen) {} /* linkage dummy */ void aprx_syslog_init(const char *p) {} int main(int argc, char **argv) { fprintf(stderr,"Sorry - aprx-stat program not available in system configured without ERLANGSTORAGE\n"); return 1; } #endif aprx-2.08.svn593/install-sh0000755000175000017500000003160012005774520014463 0ustar colincolin#!/bin/sh # install - install a program, script, or datafile scriptversion=2006-10-14.15 # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" posix_glob= posix_mkdir= # Desired mode of installed file. mode=0755 chmodcmd=$chmodprog chowncmd= chgrpcmd= stripcmd= rmcmd="$rmprog -f" mvcmd="$mvprog" src= dst= dir_arg= dstarg= no_target_directory= usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: -c (ignored) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. --help display this help and exit. --version display version info and exit. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) shift continue;; -d) dir_arg=true shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; --help) echo "$usage"; exit $?;; -m) mode=$2 shift shift case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -s) stripcmd=$stripprog shift continue;; -t) dstarg=$2 shift shift continue;; -T) no_target_directory=true shift continue;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac done if test $# -ne 0 && test -z "$dir_arg$dstarg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dstarg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dstarg" shift # fnord fi shift # arg dstarg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then trap '(exit $?); exit' 1 2 13 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src ;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dstarg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dstarg # Protect names starting with `-'. case $dst in -*) dst=./$dst ;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dstarg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix=/ ;; -*) prefix=./ ;; *) prefix= ;; esac case $posix_glob in '') if (set -f) 2>/dev/null; then posix_glob=true else posix_glob=false fi ;; esac oIFS=$IFS IFS=/ $posix_glob && set -f set fnord $dstdir shift $posix_glob && set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # Now rename the file to the real destination. { $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null \ || { # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { if test -f "$dst"; then $doit $rmcmd -f "$dst" 2>/dev/null \ || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null \ && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }; }\ || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } else : fi } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } } || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-end: "$" # End: aprx-2.08.svn593/keyhash.c0000644000175000017500000000477212305424775014300 0ustar colincolin/******************************************************************** * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * ********************************************************************/ /* * Keyhash routines for the system. * * What is needed is _fast_ hash function. Preferrably arithmethic one, * which does not need table lookups, and can work with aligned 32 bit * data -- but also on unaligned, and on any byte counts... * * Contenders: * http://burtleburtle.net/bob/c/lookup3.c * http://www.ibiblio.org/pub/Linux/devel/lang/c/mph-1.2.tar.gz * http://www.concentric.net/~Ttwang/tech/inthash.htm * http://isthe.com/chongo/tech/comp/fnv/ * * Currently using FNV-1a * */ /* // FNV-1a hash from http://isthe.com/chongo/tech/comp/fnv/ // // It is algorithmic hash without memory lookups. // Compiler seems to prefer actual multiplication over a bunch of // fixed shifts and additions. */ #include #include #include "keyhash.h" void keyhash_init(void) { } uint32_t __attribute__((pure)) keyhash(const void const *p, int len, uint32_t hash) { const uint8_t *u = p; int i; #define FNV_32_PRIME 16777619U #define FNV_32_OFFSET 2166136261U if (hash == 0) hash = (uint32_t)FNV_32_OFFSET; for (i = 0; i < len; ++i, ++u) { #if defined(NO_FNV_GCC_OPTIMIZATION) hash *= FNV_32_PRIME; #else hash += (hash<<1) + (hash<<4) + (hash<<7) + (hash<<8) + (hash<<24); #endif hash ^= (uint32_t) *u; } return hash; } /* The data material is known to contain ASCII, and if any value in there * is a lower case letter, it is first converted to upper case one. */ uint32_t __attribute__((pure)) keyhashuc(const void const *p, int len, uint32_t hash) { const uint8_t *u = p; int i; if (hash == 0) hash = (uint32_t)FNV_32_OFFSET; for (i = 0; i < len; ++i, ++u) { #if defined(NO_FNV_GCC_OPTIMIZATION) hash *= FNV_32_PRIME; #else hash += (hash<<1) + (hash<<4) + (hash<<7) + (hash<<8) + (hash<<24); #endif uint32_t c = *u; // Is it lower case ASCII letter ? if ('a' <= c && c <= 'z') { // convert to upper case. c -= ('a' - 'A'); } hash ^= c; } return hash; } aprx-2.08.svn593/aprx.conf.in0000644000175000017500000003616712306650224014720 0ustar colincolin# # Simple sample configuration file for the APRX-2 -- an APRS iGate and Digipeater # # This configuration is structured with Apache HTTPD style tags # which then contain subsystem parameters. # # # For simple case, you need to adjust 4 things: # - Mycall parameter # - passcode parameter in APRS-IS configuration # - Select correct type of interface (ax25-device or serial-device) # - Optionally set a beacon telling where this system is # - Optionally enable digipeater with or without tx-igate # # # # Define the parameters in following order: # 1) ** zero or one # 2) ** zero or one # 3) ** there can be multiple! # 4) ** zero to many # 5) ** zero to many # 6) ** zero to many (at most one for each Tx) # # # Global macro for simplified callsign definition: # Usable for 99+% of cases. # mycall N0CALL-1 # # Global macro for simplified "my location" definition in # place of explicit "lat nn lon mm" at beacons. Will also # give "my location" reference for "filter m/100". # #myloc lat ddmm.mmN lon dddmm.mmE # The aprsis login parameter: # Station callsignSSID used for relaying APRS frames into APRS-IS. # Use this only to define other callsign for APRS\-IS login. # #login OTHERCALL-7 # login defaults to $mycall # # Passcode for your callsign: # Unique code for your callsign to allow transmitting packets # into the APRS-IS. # passcode -1 # APRS-IS server name and optional portnumber. # # WARNING: Do not change from default port number [14580] # unless you are absolutely certain you want # something else, and you allow that something # else also affect your tx-igate behaviour! # server rotate.aprs2.net #server euro.aprs2.net #server asia.aprs2.net #server noam.aprs2.net #server soam.aprs2.net #server aunz.aprs2.net # Some APRS-IS servers tell every about 20 seconds to all contact # ports that they are there and alive. Others are just silent. # Default value is 3*"heartbeat" + some --> 120 (seconds) # #heartbeat-timeout 0 # Disabler of heartbeat timeout # APRS-IS server may support some filter commands. # See: http://www.aprs-is.net/javAPRSFilter.aspx # # You can define the filter as single long quoted string, or as # many short segments with explaining comments following them. # # Usability of these filters for a Tx-iGate is dubious, but # they exist in case you for example want to Tx-iGate packets # from some source callsigns in all cases even when they are # not in your local area. # #filter "possibly multiple filter specs in quotes" # #filter "m/100" # My-Range filter: positions within 100 km from my location #filter "f/OH2XYZ-3/50" # Friend-Range filter: 50 km of friend's last beacon position # pidfile is UNIX way to tell that others that this program is # running with given process-id number. This has compiled-in # default value of: pidfile @VARRUN@/aprx.pid # pidfile @VARRUN@/aprx.pid # rflog defines a rotatable file into which all RF-received packets # are logged. The host system can rotate it at any time without # need to signal the aprx that the file has been moved. # rflog @VARLOG@/aprx-rf.log # aprxlog defines a rotatable file into which most important # events on APRS-IS connection are logged, namely connects and # disconnects. The host system can rotate it at any time without # need to signal the aprx that the file has been moved. # aprxlog @VARLOG@/aprx.log # dprslog defines a rotatable file into which most important # events on DPRS receiver gateways are logged. # The host system can rotate it at any time without need to # signal the aprx that the file has been moved. # #dprslog @VARLOG@/dprs.log # erlangfile defines a mmap():able binary file, which stores # running sums of interfaces upon which the channel erlang # estimator runs, and collects data. # Depending on the system, it may be running on a filesystem # that actually retains data over reboots, or it may not. # With this backing store, the system does not loose cumulating # erlang data over the current period, if the restart is quick, # and does not stradle any exact minute. # (Do restarts at 15 seconds over an even minute..) # This file is around 0.7 MB per each interface talking APRS. # If this file is not defined and it can not be created, # internal non-persistent in-memory storage will be used. # # Built-in default value is: @VARRUN@/aprx.state # #erlangfile @VARRUN@/aprx.state # *********** Multiple definitions can follow ********* # ax25-device Lists AX.25 ports by their callsigns that in Linux # systems receive APRS packets. If none are defined, # or the system is not Linux, the AX.25 network receiver # is not enabled. Used technologies need at least # Linux kernel 2.4.x # # tx-ok Boolean telling if this device is able to transmit. # # # ax25-device $mycall # #tx-ok false # transmitter enable defaults to false # #telem-to-is true # set to 'false' to disable # # # The TNC serial options. Parameters are: # - /dev/ttyUSB1 -- tty device # - 19200 -- baud rate, supported ones are: # 1200, 2400, 4800, 9600, 19200, 38400 # - 8n1 -- 8-bits, no parity, one stop-bit, # no other supported modes # - "KISS" - plain basic KISS mode # - "XORSUM" alias "BPQCRC" - KISS with BPQ "CRC" byte # - "SMACK" alias "CRC16" - KISS with real CRC # - "FLEXNET" - KISS with real CRC # - "TNC2" - TNC2 monitor format # - "DPRS" - DPRS (RX) GW # # # serial-device /dev/ttyUSB0 19200 8n1 KISS # #callsign $mycall # callsign defaults to $mycall # #tx-ok false # transmitter enable defaults to false # #telem-to-is true # set to 'false' to disable # # # serial-device /dev/ttyUSB1 19200 8n1 TNC2 # #callsign $mycall # callsign defaults to $mycall # #tx-ok false # TNC2 monitor can not have transmitter # #telem-to-is true # set to 'false' to disable # # # serial-device /dev/ttyUSB1 19200 8n1 DPRS # callsign dprsgwcallsign # must define actual callsign # #tx-ok false # DPRS monitor can not do transmit # #telem-to-is true # set to 'false' to disable # # *********** Multiple definitions can follow ********* # # Beacons are sent out to radio transmitters AND/OR APRSIS. # Default is "both", other modes are settable. # #beaconmode { aprsis | both | radio } # # Beacons are sent from a circullar transmission queue, total cycle time # of that queue is 20 minutes by default, and beacons are "evenly" # distributed along it. Actual intervals are randomized to be anything # in between 80% and 100% of the cycle-size / number-of-beacons. # First beacon is sent out 30 seconds after system start. # Tune the cycle-size to be suitable to your number of defined beacons. # #cycle-size 20m # # Basic beaconed thing is positional message of type "!": # #beacon symbol "R&" lat "0000.00N" lon "00000.00E" comment "Rx-only iGate" #beacon symbol "R&" $myloc comment "Rx-only iGate" # #Following are basic options: # 'symbol' no default, must be defined! # 'lat' coordinate latitude: ddmm.mmN (no default!) # 'lon' coordinate longitude: dddmm.mmE (no default!) # '$myloc' coordinate values taken from global 'myloc' entry, # and usable in place of explicit 'lat'+'lon'. # 'comment' optional tail part of the item, default is nothing # # Sample symbols: # R& is for "Rx-only iGate" # I& is for "Tx-iGate" # /# is for "Digipeater" # I# is for "Tx-iGate + Digipeater"" # #Additional options are: # 'srccall' parameter sets claimed origination address. # 'dstcall' sets destination address, default "APRXnn" # 'interface' parameter picks an interface (must be "tx-ok true" type) # 'via' sets radio distribution pattern, default: none. # 'timefix' On APRS messages with HMS timestamp (hour:min:sec), the # system fixes appropriate field with transmit time timestamp. # # Message type is by default '!', which is positional no timestamp format. # Other possible formats are definable with options: # 'type' Single character setting type: ! = / @, default: ! # 'item' Defines a name of Item (')') type beacons. # 'object' Defines a name of Object (';') type beacons. # # 'file' option tells a file at which a _raw_ APRS message content is # expected to be found as first line of text. Line ending newline # is removed, and no escapes are supported. The timefix is # available, though probably should not be used. # No \-processing is done on read text line. # # 'exec' option tells a computer program which returns to stdout _raw_ APRS # message content without newline. The timefix is # available, though probably should not be used. # No \-processing is done on read text line. # # The parameter sets can vary: # a) 'srccall nnn-n dstcall "string" symbol "R&" lat "ddmm.mmN" lon "dddmm.mmE" [comment "any text"] # b) 'srccall nnn-n dstcall "string" symbol "R&" $myloc [comment "any text"] # c) 'srccall nnn-n dstcall "string" raw "string"' # # The a) form flags on some of possible syntax errors in parameters. # It will also create only "!" type messages. The dest parameter # defaults to "APRS", but can be used to give other destinations. # The via parameter can be used to add other keywords, like "NOGATE". # # Writing correct RAW format beacon message is very hard, # which is evidenced by the frequency of bad syntax texts # people so often put there... If you can not be persuaded # not to do it, then at least VERIFY the beacon result on # web service like findu.com, or aprs.fi # # Do remember that the \ -character has special treatment in the # Aprx configuration parser. If you want a '\' on APRS content, # then you encode it on configuration file as: '\\' # # Stranger combinations with explicite "transmit this to interface X": # #beacon file /tmp/wxbeacon.txt #beacon interface N0CALL-3 srccall N0CALL-3 \ # raw "!0000.00NR00000.00E&Rx-only iGate" #beacon interface N0CALL-3 srccall N0CALL-3 \ # raw "!0000.00NI00000.00E&Tx-iGate" #beacon interface $mycall symbol "R&" $myloc \ # comment "Rx-only iGate" #beacon interface $mycall symbol "I&" $myloc \ # comment "Tx-iGate" #beacon exec /usr/bin/telemetry.pl #beacon timeout 20 exec /usr/bin/telemetry.pl #beacon interface N0CALL-3 srccall N0CALL-3 \ # timeout 20 exec /usr/bin/telemetry.pl # # *********** definition(s) follow ********* # # The system will always send telemetry for all of its interfaces # to APRSIS, but there is an option to define telemetry to be sent # to radio channel by using following sections for each transmitter # that is wanted to send out the telemetry. # # transmitter - callsign referring to # via - optional via-path, only 1 callsign! # source - one or more of callsigns for which # the telemetry transmission is wanted for # # # transmitter $mycall # via TRACE1-1 # source $mycall # # *********** definition(s) follow ********* # # The digipeater definitions tell transmitters that receive # AX.25 packets from possibly multiple sources, and then what # to do on the AX.25 headers of those messages. # # There is one transmitter per digipeater -- and inversely, there # can be at most one digipeater for each transmitter. # # In each digipeater there is at least one , usually same # as the transmitter. You may use same on multiple # s. Using multiple instances of same on # a single does not crash the system, but it can cause # packet duplication in case of non-APRS protocols (like AX.25 CONS) # # Use only at most two levels of viscous-delay in your . # Immediate sending is by "0", and a delayed sending is any value # from 1 to 9. This system does not correctly support other than # immediate sending and one level of delay. # # Note: In order to igate correct when multiple receivers and # transmitters are used on single channel, the # definitions of each radio port must have associated # "igate-group N" parameter which has N of value 1 to 3. # See the aprx-manual.pdf for details. # (Default software compilation allows you to have up to # three channels of APRS operation.) # # # transmitter $mycall # #ratelimit 60 120 # default: average 60 packets/minute, # # # burst max 120 packets/minute # #srcratelimit 10 20 # Example: by sourcecall: # # average 10 packets/minute, # # burst max 20 packets/minute # # # source $mycall # # #relay-type digipeated # default mode is "digipeated" # # viscous-delay 0 # no viscous delay for RF->RF digipeating # # ratelimit 60 120 # default: average 60 packets/minute, # # # burst max 120 packets/minute # ## filter a/la/lo/la/lo # service area filter # ## filter -b/CALL # always block these # # # # Diversity receiver which combines to the primary # # Tx/Rx transmitter. There can be as many of these # # as you can connect on this machine. # # # # source RXPORT-1 # # #relay-type digipeated # default mode is "digipeated" # # viscous-delay 0 # no viscous delay for RF->RF digipeating # # ratelimit 60 120 # default: average 60 packets/minute, # # # burst max 120 packets/minute # ## filter a/la/lo/la/lo # service area filter # ## filter -b/CALL # always block these # # # # # APRSIS source adds a TX-IGATE behaviour # # source APRSIS # # relay-type third-party # Must define this for APRSIS source! # # viscous-delay 5 # Recommendation: 5 seconds delay to give # # # RF delivery time make itself known. # # ratelimit 60 120 # default: average 60 packets/minute, # # # burst max 120 packets/minute # ## filter a/la/lo/la/lo # service area filter # ## filter -b/CALL # always block these # # # # # # DPRS source adds a DPRS->APRS RF gate # # interface DPRS # # ratelimit 60 120 # default: average 60 packets/minute, # # # burst max 120 packets/minute # # relay-type third-party # Must define this for DPRS source! # # # aprx-2.08.svn593/aprxpolls.c0000644000175000017500000000262412305424775014662 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" /* aprxpolls libary functions.. */ void aprxpolls_reset(struct aprxpolls *app) { app->pollcount = 0; } int aprxpolls_millis(struct aprxpolls *app) { return tv_timerdelta_millis(&tick,&app->next_timeout); } struct pollfd *aprxpolls_new(struct aprxpolls *app) { struct pollfd *p; app->pollcount += 1; if (app->pollcount >= app->pollsize) { app->pollsize += 8; app->polls = realloc(app->polls, sizeof(struct pollfd) * app->pollsize); // valgrind polishing.. p = &(app->polls[app->pollcount - 1]); memset(p, 0, sizeof(struct pollfd) * 8); } assert(app->polls); p = &(app->polls[app->pollcount - 1]); memset(p, 0, sizeof(struct pollfd)); return p; } void aprxpolls_free(struct aprxpolls *app) { free(app->polls); app->polls = NULL; } aprx-2.08.svn593/igate.c0000644000175000017500000004216612305424775013734 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * IGATE: Pass packets in between RF network and APRS-IS * * * * **************************************************************** */ #include "aprx.h" const char *tnc2_verify_callsign_format(const char *t, int starok, int strictax25, const char *e) { const char *s = t; for (; *s && s < e; ++s) { /* Valid station-id charset is: [A-Z0-9] */ int c = *s; if (!(('A' <= c && c <= 'Z') || ('0' <= c && c <= '9'))) { /* Not A-Z, 0-9 */ break; } } /* Now *s can be any of: '>,*-:' */ if (*s == '-') { /* Minus and digits.. */ ++s; if (strictax25) { if ('1' <= *s && *s <= '9') ++s; if ('0' <= *s && *s <= '9') ++s; } else { // Up to 2 of any alphanumeric if (('0' <= *s && *s <= '9') || ('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z')) ++s; if (('0' <= *s && *s <= '9') || ('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z')) ++s; } } if (*s == '*' /* && starok */ ) /* Star is present at way too many SRC and DEST addresses, it is not limited to VIA fields :-( */ ++s; if (s > e) { if (debug) printf("callsign scanner ran over end of buffer"); return NULL; /* Over the end-of-buffer */ } if (s == t) { if (debug) printf("%s callsign format verify got bad character: '%c' in string: '%.20s'\n", strictax25 ? "Strict":"Lenient", *s, t); return NULL; /* Too short ? */ } if (*s != '>' && *s != ',' && *s != ':' && *s != 0) { /* Terminates badly.. */ if (debug) printf("%s callsign format verify got bad character: '%c' in string: '%.20s'\n", strictax25 ? "Strict":"Lenient", *s, t); return NULL; } return s; } #ifndef DISABLE_IGATE /* * igate start -- make TX-igate allocations and inits */ void igate_start() { // Always relay all traffic from RF to APRSIS, other // direction is handled per transmitter interface... // enable_aprsis_rx_dupecheck(); } static const char *tnc2_forbidden_source_stationid(const char *t, const int strictax25,const char *e) { const char *s; s = tnc2_verify_callsign_format(t, 0, strictax25, e); if (!s) return NULL; if (memcmp("WIDE", t, 4) == 0 || /* just plain wrong setting */ memcmp("RELAY", t, 5) == 0 || /* just plain wrong setting */ memcmp("TRACE", t, 5) == 0 || /* just plain wrong setting */ memcmp("TCPIP", t, 5) == 0 || /* just plain wrong setting */ memcmp("TCPXX", t, 5) == 0 || /* just plain wrong setting */ memcmp("N0CALL", t, 6) == 0 || /* TNC default setting */ memcmp("NOCALL", t, 6) == 0) /* TNC default setting */ return NULL; return s; } static const char *tnc2_forbidden_destination_stationid(const char *t, const int strictax25, const char *e) { const char *s; s = tnc2_verify_callsign_format(t, 0, strictax25, e); if (!s) return NULL; if (memcmp("TCPIP", t, 5) == 0 || /* just plain wrong */ memcmp("TCPXX", t, 5) == 0 || /* Forbidden to gate */ memcmp("NOGATE", t, 5) == 0 || /* Forbidden to gate */ memcmp("RFONLY", t, 5) == 0 || /* Forbidden to gate */ memcmp("N0CALL", t, 6) == 0 || /* TNC default setting */ memcmp("NOCALL", t, 6) == 0) /* TNC default setting */ return NULL; return s; } static const char *tnc2_forbidden_via_stationid(const char *t, const int strictax25, const char *e) { const char *s; s = tnc2_verify_callsign_format(t, 1, strictax25, e); if (!s) return NULL; if (memcmp("RFONLY", t, 6) == 0 || memcmp("NOGATE", t, 6) == 0 || memcmp("TCPIP", t, 5) == 0 || memcmp("TCPXX", t, 5) == 0) return NULL; return s; } /* static int tnc2_forbidden_data(const char *t) { int i; for (i = 0; i < dataregscount; ++i) { int stat = regexec(dataregs[i], t, 0, NULL, 0); if (stat == 0) return 1; // MATCH! } return 0; } */ void verblog(const char *portname, int istx, const char *tnc2buf, int tnc2len) { if (verbout) { printf("%ld\t%-9s ", (long) tick.tv_sec, portname); printf("%s \t", istx ? "T":"R"); fwrite(tnc2buf, tnc2len, 1, stdout); printf("\n"); } } /* * The tnc2_rxgate() is actual RX-iGate filter function, and processes * prepated TNC2 format text presentation of the packet. * It does presume that the record is in a buffer that can be written on! */ void igate_to_aprsis(const char *portname, const int tncid, const char *tnc2buf, int tnc2addrlen, int tnc2len, const int discard0, const int strictax25_) { const char *tp, *t, *t0; const char *s; const char *ae; const char *e; int discard = discard0; int strictax25 = strictax25_; // Callsigns per strict AX25 (not 3rd-party) tp = tnc2buf; // 3rd-party recursion moves tp ae = tp + tnc2addrlen; // 3rd-party recursion moves ae e = tp + tnc2len; // stays the same all the time rflog(portname, 'R', discard, tp, tnc2len); redo_frame_filter:; t = tp; t0 = NULL; /* t == beginning of the TNC2 format packet */ /* * If any of following matches, discard the packet! * next if ($axpath =~ m/^WIDE/io); # Begins with = is sourced by.. * next if ($axpath =~ m/^RELAY/io); * next if ($axpath =~ m/^TRACE/io); */ s = tnc2_forbidden_source_stationid(t, strictax25, e); if (s) t = (char *) s; else { /* Forbidden in source fields.. */ if (debug) printf("TNC2 forbidden source stationid: '%.20s'\n", t); goto discard; } /* SOURCE>DESTIN,VIA,VIA:payload */ if (*t == '>') { ++t; } else { if (debug) printf("TNC2 bad address format, expected '>', got: '%.20s'\n", t); goto discard; } s = tnc2_forbidden_destination_stationid(t, strictax25, e); if (s) t = (char *) s; else { if (debug) printf("TNC2 forbidden (by REGEX) destination stationid: '%.20s'\n", t); goto discard; } while (*t && t < ae) { if (*t == ',') { ++t; } else { if (debug) printf("TNC2 via address syntax bug, wanted ',' or ':', got: '%.20s'\n", t); goto discard; } /* * next if ($axpath =~ m/RFONLY/io); # Has any of these in via fields.. * next if ($axpath =~ m/TCPIP/io); * next if ($axpath =~ m/TCPXX/io); * next if ($axpath =~ m/NOGATE/io); # .. drop it. */ s = tnc2_forbidden_via_stationid(t, strictax25, e); if (!s) { /* Forbidden in via fields.. */ if (debug) printf("TNC2 forbidden VIA stationid, got: '%.20s'\n", t); goto discard; } else t = (char *) s; } /* Now we have processed the address, this should be ABORT time if the current character is not ':' ! */ if (*t == ':') { #if 0 // *t++ = 0; /* turn it to NUL character */ #else /* Don't zero! */ ++t; #endif ; } else { if (debug) printf("TNC2 address parsing did not find ':': '%.20s'\n",t); goto discard; } t0 = t; // Start of payload /* Now 't' points to data.. */ /* if (tnc2_forbidden_data(t)) { if (debug) printf("Forbidden data in TNC2 packet - REGEX match"); goto discard; } */ /* Will not relay messages that begin with '?' char: */ if (*t == '?') { if (debug) printf("Will not relay packets where payload begins with '?'\n"); goto discard; } /* Messages begining with '}' char are 3rd-party frames.. */ if (*t == '}') { /* DEBUG OUTPUT TO STDOUT ! */ verblog(portname, 0, tp, tnc2len); strictax25 = 0; /* Copy the 3rd-party message content into begining of the buffer... */ ++t; /* Skip the '}' */ tp = t; tnc2len = e - t; /* New length */ // end pointer (e) does not change // Address end must be searched again ae = memchr(tp, ':', tnc2len); if (ae == NULL) { // Bad 3rd-party frame goto discard; } tnc2addrlen = (int)(ae - tp); /* .. and redo the filtering. */ goto redo_frame_filter; } /* TODO: Verify message being of recognized APRS packet type */ /* '\0x60', '\0x27': MIC-E, len >= 9 * '!','=','/','{': Normal or compressed location packet.. * '$': NMEA data, if it begins as '$GP' * '$': WX data (maybe) if not NMEA data * ';': Object data, len >= 31 * ')': Item data, len >= 18 * ':': message, bulletin or aanouncement, len >= 11 * '<': Station Capabilities, len >= 2 * '>': Status report * '}': Third-party message * ... and many more ... */ // FIXME: Duplicate filter messages to APRSIS /* _NO_ ending CRLF, the APRSIS subsystem adds it. */ /* printf("alen=%d tlen=%d tnc2buf=%s\n",t0-1-tnc2buf, e-t0, tnc2buf); */ discard = aprsis_queue(tp, tnc2addrlen, qTYPE_IGATED, portname, t0, e - t0); /* Send it.. */ /* DEBUG OUTPUT TO STDOUT ! */ verblog(portname, 0, tp, tnc2len); // Log the innermost form of packet to be sent out.. if (tp != tnc2buf || discard != discard0) rflog(portname, 'R', discard, tp, tnc2len); if (0) { discard:; discard = -1; } if (discard) { erlang_add(portname, ERLANG_DROP, tnc2len, 1); } } /* ---------------------------------------------------------- */ /* * Study APRS-IS received message's address header part * to determine if it is not to be relayed back to RF.. */ static int forbidden_to_gate_addr(const char *s) { if (memcmp(s, "TCPXX", 5) == 0) return 1; /* Forbidden to be relayed */ if (memcmp(s, "NOGATE", 6) == 0) return 1; /* Forbidden to be relayed */ if (memcmp(s, "RFONLY", 6) == 0) return 1; /* Forbidden to be relayed */ if (memcmp(s, "qAX", 3) == 0) return 1; return 0; /* Found nothing forbidden */ } /* * For APRSIS -> APRX -> RF gatewaying. * Have to convert incoming TNC2 format messge to AX.25.. * * See: http://www.aprs-is.net/IGateDetails.aspx * * ---------------------------------------------------------------- * * Gate message packets and associated posits to RF if * * 1. the receiving station has been heard within range within * a predefined time period (range defined as digi hops, * distance, or both). * 2. the sending station has not been heard via RF within * a predefined time period (packets gated from the Internet * by other stations are excluded from this test). * 3. the sending station does not have TCPXX, NOGATE, or RFONLY * in the header. * 4. the receiving station has not been heard via the Internet * within a predefined time period. * * A station is said to be heard via the Internet if packets from * the station contain TCPIP* or TCPXX* in the header or if gated * (3rd-party) packets are seen on RF gated by the station and * containing TCPIP or TCPXX in the 3rd-party header (in other * words, the station is seen on RF as being an IGate). * * Gate all packets to RF based on criteria set by the sysop (such * as callsign, object name, etc.). * * ---------------------------------------------------------------- * * TODO: * a) APRS-IS relayed third-party frames are ignored. * * 3) The message path does not have TCPXX, NOGATE, RFONLY * in it. * * Following steps are done in interface_receive_3rdparty() * * 1) The receiving station has been heard recently * within defined range limits, and more recently * than since given interval T1. (Range as digi-hops [N1] * or coordinates, or both.) * * 2) The sending station has not been heard via RF * within timer interval T2. (Third-party relayed * frames are not analyzed for this.) * * 4) the receiving station has not been heard via the Internet * within a predefined time period. * A station is said to be heard via the Internet if packets * from the station contain TCPIP* or TCPXX* in the header or * if gated (3rd-party) packets are seen on RF gated by the * station and containing TCPIP or TCPXX in the 3rd-party * header (in other words, the station is seen on RF as being * an IGate). * * 5) Gate all packets to RF based on criteria set by the sysop * (such as callsign, object name, etc.). * * c) Drop everything else. * * Paths * * IGates should use the 3rd-party format on RF of * IGATECALL>APRS,GATEPATH}FROMCALL>TOCALL,TCPIP,IGATECALL*:original packet data * where GATEPATH is the path that the gated packet is to follow * on RF. This format will allow IGates to prevent gating the packet * back to APRS-IS. * * q constructs should never appear on RF. * The I construct should never appear on RF. * Except for within gated packets, TCPIP and TCPXX should not be * used on RF. */ static void pick_heads(char *ax25, int headlen, char *heads[20], int *headscount) { char *p = ax25; char *e = ax25 + headlen; char *p0 = p; // if (debug)printf(" head parse: "); while (p <= e) { p0 = p; while (p <= e) { const char c = *p; if (c != '>' && c != ',' && c != ':') { ++p; continue; } *p++ = 0; if (*headscount >= 19) continue; /* too many head parts.. */ heads[*headscount] = p0; *headscount += 1; // if (debug) printf(" %-9s", p0); break; } } heads[*headscount] = NULL; // if (debug)printf("\n"); } static void aprsis_commentframe(const char *tnc2buf, int tnc2len) { // TODO .. #TICK -> #TOCK ?? } void igate_from_aprsis(const char *ax25, int ax25len) { // const char *p = ax25; int colonidx; int ok_to_relay, i; const char *b; // const char *e = p + ax25len; /* string end pointer */ // char axbuf[3000]; /* enough and then some more.. */ // char axbuf2[1000]; /* enough and then some more.. */ char *heads[20]; char *headsbuf; int headscount = 0; // char *s; char *fromcall = NULL; char *origtocall = NULL; if (ax25[0] == '#') { // Comment line, timer tick, something such... aprsis_commentframe(ax25, ax25len); return; } if (ax25len > 520) { /* Way too large a frame... */ if (debug)printf("APRSIS dataframe length is too large! (%d)\n",ax25len); return; } b = memchr(ax25, ':', ax25len); if (b == NULL) { if (debug)printf("APRSIS dataframe does not have ':' in it\n"); return; // Huh? No double-colon on line, it is not proper packet line } colonidx = b-ax25; if (colonidx+3 >= ax25len) { /* Not really any data there.. */ if (debug)printf("APRSIS dataframe too short to contain anything\n"); return; } rflog("APRSIS",'R',0,ax25, ax25len); headsbuf = alloca(colonidx+1); memcpy(headsbuf, ax25, colonidx+1); headscount = 0; pick_heads(headsbuf, colonidx, heads, &headscount); ok_to_relay = 0; if (headscount < 4) { // Less than 3 header fields coming from APRS-IS ? if (debug) printf("Not relayable packet! [1]\n"); return; } if (memcmp(heads[1],"RXTLM-",6)==0) { if (debug) printf("Not relayable packet! [2]\n"); return; } fromcall = heads[0]; origtocall = heads[1]; for (i = 0; i < headscount; ++i) { /* 3) */ if (forbidden_to_gate_addr(heads[i])) { if (debug) printf("Not relayable packet! [3]\n"); return; } // FIXME: Hmm.. Really ?? q-construct analysis and "ok_to_relay" decission if (heads[i][0] == 'q' && heads[i][1] == 'A') { int qcode = heads[i][2]; ok_to_relay = 1; // Depending on qA? value, following may // actually be fromcall, or some other.. switch (qcode) { case 'X': ok_to_relay = 0; break; default: break; } heads[i] = NULL; break; } } if (!ok_to_relay) { if (debug) printf("Not relayable packet! [4]\n"); return; } ++b; /* Skip the ':' */ /* a) */ /* Check for forbidden things that cause dropping the packet */ if (*b == '}') { /* Third-party packet from APRS-IS */ if (debug) printf("Not relayable packet! [5]\n"); return; /* drop it */ } // Following logic steps are done in interface_receive_3rdparty! // FIXME: 1) - verify receiving station has been heard recently on radio // FIXME: 2) - sending station has not been heard recently on radio // FIXME: 4) - the receiving station has not been heard via the Internet within a predefined time period. // FIXME: f) - ?? if (debug) printf(".. igate from aprsis\n"); interface_receive_3rdparty( &aprsis_interface, fromcall, origtocall, "TCPIP", b, ax25len - (b-ax25) ); } #endif /* ---------------------------------------------------------- */ void rflog(const char *portname, char direction, int discard, const char *tnc2buf, int tnc2len) { if (rflogfile) { FILE *fp = NULL; if (strcmp("-",rflogfile)==0) { if (debug < 2) return; fp = stdout; } else { fp = fopen(rflogfile, "a"); } if (fp) { char timebuf[60]; printtime(timebuf, sizeof(timebuf)); (void)fprintf(fp, "%s %-9s ", timebuf, portname); (void)fprintf(fp, "%c ", direction); if (discard < 0) { fprintf(fp, "*"); } if (discard > 0) { fprintf(fp, "#"); } (void)fwrite( tnc2buf, tnc2len, 1, fp); (void)fprintf( fp, "\n" ); if (fp != stdout) fclose(fp); } } } aprx-2.08.svn593/config.h.in0000644000175000017500000001113412314016324014474 0ustar colincolin/* config.h.in. Generated from configure.in by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* Configuration command line */ #undef CONFIGURE_CMD /* Define to 1 if you want to disable all IGATE codes. */ #undef DISABLE_IGATE /* Define for pthread(3p) disabling */ #undef DISABLE_PTHREAD /* Define for an embedded target */ #undef EMBEDDED /* Define to 1 if you want to enable AGWPE socket interface. */ #undef ENABLE_AGWPE /* Define for pthread(3p) enabling */ #undef ENABLE_PTHREAD /* Define for a non-embedded system with filesystem based Erlang history storage */ #undef ERLANGSTORAGE /* Define to 1 if you have the header file. */ #undef HAVE_ALLOCA_H /* Define to 1 if you have the `atan2f' function. */ #undef HAVE_ATAN2F /* Have clock_gettime */ #undef HAVE_CLOCK_GETTIME /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ #undef HAVE_DOPRNT /* Define to 1 if you have the `getaddrinfo' function. */ #undef HAVE_GETADDRINFO /* Define to 1 if you have the `gettimeofday' function. */ #undef HAVE_GETTIMEOFDAY /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `memchr' function. */ #undef HAVE_MEMCHR /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `memrchr' function. */ #undef HAVE_MEMRCHR /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_SCTP_H /* Define to 1 if you have the `openpty' function. */ #undef HAVE_OPENPTY /* Define to 1 if you have the header file. */ #undef HAVE_OPENSSL_SSL_H /* Define to 1 if you have the header file. */ #undef HAVE_POLL_H /* Have pthread_create() function */ #undef HAVE_PTHREAD_CREATE /* Define to 1 if you have the header file. */ #undef HAVE_PTHREAD_H /* Define to 1 if you have the header file. */ #undef HAVE_PTY_H /* Define to 1 if you have the `socket' function. */ #undef HAVE_SOCKET /* Define to 1 if you have the `socketpair' function. */ #undef HAVE_SOCKETPAIR /* Define to 1 if you have the header file. */ #undef HAVE_STDARG_H /* Define to 1 if you have the header file. */ #undef HAVE_STDDEF_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_EPOLL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_WAIT_H /* Define to 1 if you have the header file. */ #undef HAVE_TIME_H /* OpenSSL 0.9.7 or later */ #undef HAVE_TLSV1_SERVER_METHOD /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the header file. */ #undef HAVE_VARARGS_H /* Define to 1 if you have the `vprintf' function. */ #undef HAVE_VPRINTF /* OpenSSL 0.9.7 or later */ #undef HAVE_X509_FREE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* The size of `double', as computed by sizeof. */ #undef SIZEOF_DOUBLE /* The size of `int', as computed by sizeof. */ #undef SIZEOF_INT /* The size of `long', as computed by sizeof. */ #undef SIZEOF_LONG /* The size of `short', as computed by sizeof. */ #undef SIZEOF_SHORT /* The size of `void *', as computed by sizeof. */ #undef SIZEOF_VOID_P /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif aprx-2.08.svn593/TODO0000644000175000017500000001012212313325035013136 0ustar colincolin APRX 2.08 TODO / Wishlist - "[Aprx] Re: WIDE2-2/NOGATE - trace not working?" - "[Aprx] Re: Bi-Directional Cross-band Digipeater" Probably heard sources accounting bucket initializing as zero, which was fixed recently. - SSL mode to talk to APRS-IS. (Aprsc speaks SSL very efficiently.) - SCTP communication protocol to APRS-IS matching Aprsc's code - Embedded Perl and/or Python? That would allow folks to cook up arbitrary action scripts for who knows what purposes... - Socket API to send/receive/monitor packets? To get Protocol Buffer or JSON/BSON wrapped datasets of parsed frame including lat/lon of the packet, type info, etc. ? - BPQ32 protocol? - AWGPE protocol? - Copy-of-APRSIS ? (okay(ish) for monitor + transmit to APRS-IS..) - Message destined to $MYCALL to be logged at at syslog INFO level, and acknowledged(?) - Tx-IGate filter rules (interface.c) Tx-IGate is lacking following things, but it is already doing most important ones ('recipient heard recently in my service area') - figure out (at parse_aprs() ?) the number of hops a packet has traversed so far, and log that on historydb (for recipient "locality" analysis) - test that message recipients known coordinates are within "my service area" --> separate filter from adjunct-style areas or distances? - for position-less packets, send at first a position packet for same source callsign, if available - Digipeater behaviour on "requested wideness exceeds allowed max limit" and this is a directly heard packet: - now mark all H-bits set - ADDITION: inject transmitter callsign into first VIA slot. - Digipeating details of non-APRS UI protocols? Some way to handle "WIDEn-N" on selected PIDs? - OpenTRAC support ? (UI, PID=0x77) - Yes, on DIGIPEATER ONLY - what about WIDEn-N ? Y/N ? - Windows port? - Colorize debug printout (or make another tool to colorize aprx-rf.log) - rx-packets green (or blue), tx-packets red - port names/callsigns bolded, same with packet source callsigns - Use internal APRS packet parser to validate beacon configurations. This should be able to flag bad input on RAW beacon data. - Calculate and note APRS DXes (like digi_ned does) ? - Calculate ALOHA range, and publish it? - Embedded SNTP client? (Reads NTP time from 127.0.0.1:123 - or configured external source) Supports Aprx-Aprx clustered protocol where all messages are equipped with NTP timestamp and if in receiver's opinnion the time is too far off, the network delays have been excessive and message is discarded. This SNTP client _does_ _not_ adjust host system clock, only tracks difference of it against a high(er)-quality NTP service elsewere. The difference and drift parameters are used to convert current system gettimeofday() result to NTP timestamp within the code, and to determine how old something is, a NTP timestamp is compared against "current time." If Aprx System has no time at all, one SNTP probe is made to determine "initial system time". This SNTP client will have two time management states: - "current time" - "new time" If SNTP probes return times that agree with "current time", the "new time" accumulator is discarded, and possible minor drift sync is done on "current time." If SNTP probes return times that do not agree with "current time", then a "new time" accumulator is used to track the time. If the "new time" is consistently tracked for 5 samples, "new time" becomes "current time". BoB: Shucks, it would sure be nice if EVERY DIGIPEATER also had a little smarts and could also report on its own RSSI. That would give some people a clue what the digipeater is hearing on its input. In its 10 minute beacon it could report: "176RX, 109TX, 243 busy" ON a ten minute basis (total 600 1 sec slots) this tells us that 176 packets were decoded at this site, 109 were elegible for further digipeating, and another 243 seconds the channel was busy with other collisions and non decodable traffic. Leaving 72 secnds the channel was clear. by Matti Aarnio - OH2MQK - oh2mqk-at-sral-fi - 2007-2014 aprx-2.08.svn593/filter.c.2.06-to-head.diff0000644000175000017500000004245312307073061016727 0ustar colincolinIndex: filter.c =================================================================== --- filter.c (revision 507) +++ filter.c (working copy) @@ -3,7 +3,7 @@ * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * - * (c) Matti Aarnio - OH2MQK, 2007-2012 * + * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * ********************************************************************/ /* @@ -30,6 +30,7 @@ d/digi1/digi2... Digipeater filter (*) e/call1/call1/... Entry station filter (*) f/call/dist Friend Range filter + g/call1/call2.. Group Messaging filter (*) m/dist My Range filter o/obj1/obj2... Object filter (*) p/aa/bb/cc... Prefix filter @@ -36,8 +37,8 @@ q/con/ana q Contruct filter r/lat/lon/dist Range filter s/pri/alt/over Symbol filter - t/poimntqsu*c Type filter - t/poimntqsu*c/call/km Type filter + t/poimntqsu3*c Type filter + t/poimntqsu3*c/call/km Type filter u/unproto1/unproto2/.. Unproto filter (*) Sample usage frequencies (out of entire APRS-IS): @@ -338,7 +339,7 @@ if (f->len == keylen) { int cmp = strncasecmp(f->callsign, uckey, keylen); if (cmp == 0) { /* Have key match */ - f->expirytime = now.tv_sec + filter_entrycall_maxage; + f->expirytime = tick.tv_sec + filter_entrycall_maxage; f2 = f; break; } @@ -356,11 +357,11 @@ #ifndef _FOR_VALGRIND_ f = cellmalloc(filter_entrycall_cells); #else - f = malloc(sizeof(*f)); + f = calloc(1, sizeof(*f)); #endif if (f) { f->next = *fp; - f->expirytime = now.tv_sec + filter_entrycall_maxage; + f->expirytime = tick.tv_sec + filter_entrycall_maxage; f->hash = hash; f->len = keylen; memcpy(f->callsign, uckey, keylen); @@ -405,7 +406,7 @@ int rc = strncasecmp(f->callsign, key, keylen); if (rc == 0) { /* Have key match, see if it is still valid entry ? */ - if (f->expirytime < now.tv_sec - 60) { + if ((f->expirytime - (tick.tv_sec - 60)) < 0) { f2 = f; break; } @@ -432,7 +433,7 @@ fp = & filter_entrycall_hash[k]; while (( f = *fp )) { /* Did it expire ? */ - if (f->expirytime <= now.tv_sec) { + if ((f->expirytime - tick.tv_sec) <= 0) { *fp = f->next; f->next = NULL; filter_entrycall_free(f); @@ -539,7 +540,7 @@ if (f->len == keylen) { int cmp = memcmp(f->callsign, uckey, keylen); if (cmp == 0) { /* Have key match */ - f->expirytime = now.tv_sec + filter_wx_maxage; + f->expirytime = tick.tv_sec + filter_wx_maxage; f2 = f; break; } @@ -557,12 +558,12 @@ #ifndef _FOR_VALGRIND_ f = cellmalloc(filter_wx_cells); #else - f = malloc(sizeof(*f)); + f = calloc(1, sizeof(*f)); #endif ++filter_wx_cellgauge; if (f) { f->next = *fp; - f->expirytime = now.tv_sec + filter_wx_maxage; + f->expirytime = tick.tv_sec + filter_wx_maxage; f->hash = hash; f->len = keylen; memcpy(f->callsign, key, keylen); @@ -593,7 +594,7 @@ int rc = strncasecmp(f->callsign, key, keylen); if (rc == 0) { /* Have key match, see if it is still valid entry ? */ - if (f->expirytime < now.tv_sec - 60) { + if ((f->expirytime - (tick.tv_sec - 60)) < 0) { f2 = f; break; } @@ -621,7 +622,7 @@ fp = & filter_wx_hash[k]; while (( f = *fp )) { /* Did it expire ? */ - if (f->expirytime <= now.tv_sec) { + if ((f->expirytime - tick.tv_sec) <= 0) { *fp = f->next; f->next = NULL; filter_wx_free(f); @@ -804,13 +805,27 @@ /* count the number of prefixes in there.. */ while (*p) { if (*p) ++refmax; - while (*p && *p != '/' && *p != ',') ++p; - if (*p == '/' || *p == ',') ++p; + while (*p && *p != '/') ++p; + if (*p == '/') ++p; } - if (refmax == 0) return -1; /* No prefixes ?? */ + if (refmax == 0) { + printf("Filter definition of '%s' has no prefixes defined.\n", filt0); + return -1; /* No prefixes ?? */ + } - refbuf = malloc(sizeof(*refbuf)*refmax); - refcount = 0; + if (ff && ff->h.type == f0->h.type) { /* SAME TYPE, + extend previous record! */ + extend = 1; + refcount = ff->h.u3.numnames + refmax; + refbuf = realloc(ff->h.u5.refcallsigns, sizeof(*refbuf) * refcount); + ff->h.u5.refcallsigns = refbuf; + refcount = ff->h.u3.numnames; + } else { + refbuf = calloc(1, sizeof(*refbuf)*refmax); + refcount = 0; + f0->h.u5.refcallsigns = refbuf; + f0->h.u3.numnames = 0; + } p = filt0; if (*p == '-') ++p; @@ -824,24 +839,28 @@ memset(prefixbuf, 0, sizeof(prefixbuf)); i = 0; wildcard = 0; - while (*p != 0 && *p != '/' && *p != ',') { + while (*p != 0 && *p != '/') { int c = *p++; if (c == '*') { - wildcard = 1; - if (wildok != MatchWild) - return -1; - continue; + wildcard = 1; + if (wildok != MatchWild) { + printf("Wild-card matching not permitted, yet filter definition says: '%s'\n", filt0); + return -1; + } + continue; } - ++i; - if (i < (CALLSIGNLEN_MAX)) { - *k = c; - ++k; + if (i < CALLSIGNLEN_MAX) { + *k++ = c; + ++i; + } else { + printf("Too long callsign string: '%s' input: '%s'\n", prefixbuf, filt0); + return -1; // invalid input } } *k = 0; /* OK, we have one prefix part collected, scan source until next '/' */ - if (*p != 0 && *p != '/' && *p != ',') ++p; - if (*p == '/' || *p == ',') ++p; + if (*p != 0 && *p != '/') ++p; + if (*p == '/') ++p; /* If there is more of patterns, the loop continues.. */ /* Store the refprefix */ @@ -855,11 +874,10 @@ ++refcount; } - f0->h.u5.refcallsigns = refbuf; - f0->h.u3.numnames = refcount; + f0->h.u3.numnames = refcount; if (extend) { char *s; - ff->h.u3.numnames = refcount; + ff->h.u3.numnames = refcount; i = strlen(ff->h.text) + strlen(filt0)+2; if (i <= FILT_TEXTBUFSIZE) { /* Fits in our built-in buffer block - like previous.. @@ -1081,7 +1099,6 @@ break; -#if 0 case 'd': case 'D': /* d/digi1/digi2... Digipeater filter (*) */ @@ -1093,7 +1110,6 @@ return 0; break; -#endif #if 0 case 'e': case 'E': @@ -1133,11 +1149,31 @@ */ break; -#if 0 + + case 'g': + case 'G': + // g/call1/call2/ Group Messaging filter + i = filter_parse_one_callsignset(ffp, &f0, filt0, MatchWild ); + if (i < 0) + return i; + if (i > 0) /* extended previous */ + return 0; + break; + case 'm': case 'M': /* m/dist My range filter */ + if (myloc_latstr == NULL) { + printf("The M/radius_km filter requires top-level myloc definition. It doesn't exist.\n"); + return -1; + } + + f0.h.type = 'r'; // internal implementation at Aprx is a RANGE filter. + f0.h.f_latN = myloc_lat; // radians + f0.h.f_lonE = myloc_lon; // radians + f0.h.u1.f_coslat = myloc_coslat; + i = sscanf(filt+1, "/%f", &f0.h.u2.f_dist); if (i != 1 || f0.h.u2.f_dist < 0.1) { // hlog(LOG_DEBUG, "Bad filter parse: %s", filt0); @@ -1149,7 +1185,7 @@ // hlog(LOG_DEBUG, "Filter: %s -> M %.3f", filt0, f0.h.u2.f_dist); break; -#endif + case 'o': case 'O': /* o/obje1/obj2... Object filter (*) */ @@ -1315,6 +1351,9 @@ case '*': f0.h.u4.bitflags |= ~T_CWOP; /* "ALL" -- excluding CWOP */ break; + case '3': + f0.h.u4.bitflags |= T_THIRDPARTY; + break; case 'c': case 'C': f0.h.u4.bitflags |= T_CWOP; break; @@ -1409,7 +1448,7 @@ } else f->h.text = strdup(filt0); /* and copy of filter text */ #else - f = malloc(sizeof(*f) + strlen(filt0)); + f = calloc(1, sizeof(*f) + strlen(filt0)); *f = f0; /* store pre-parsed values */ f->h.text = f->textbuf; strcpy(f->textbuf, filt); /* and copy of filter text */ @@ -1556,22 +1595,21 @@ if (i > CALLSIGNLEN_MAX) i = CALLSIGNLEN_MAX; /* source address "addr">... */ - memset( ref.callsign, 0, sizeof(ref.callsign)); + memset( &ref, 0, sizeof(ref) ); // clear it all memcpy( ref.callsign, pb->data, i); return filter_match_on_callsignset(&ref, i, f, MatchWild); } -#if 0 static int filter_process_one_d(struct pbuf_t *pb, struct filter_t *f) { /* d/digi1/digi2... Digipeater filter - The digipeater filter will pass all packets that have been + The digipeater filter will match all packets that have been digipeated by a particular station(s) (the station's call is in the path). This filter allows the * wildcard. - 25-35 filters in use at any given time. + 25-35 instances in APRS-IS core at any given time. Up to 1300 invocations per second. */ struct filter_refcallsign_t ref; @@ -1585,9 +1623,9 @@ for (i = 0; d < q; ) { ++j; - if (j > 10) break; /* way too many callsigns... */ + if (j > 10) break; // way too many callsigns... (code bug?) - if (*d == ',') ++d; /* second round and onwards.. */ + if (*d == ',') ++d; // second round and onwards.. for (i = 0; i+d <= q && i <= CALLSIGNLEN_MAX; ++i) { if (d[i] == ',') break; @@ -1595,9 +1633,9 @@ // hlog(LOG_INFO, "d: -> (%d,%d) '%.*s'", (int)(d-pb->data), i, i, d); - /* digipeater address ",addr," */ + // digipeater address ",addr," + memset( &ref, 0, sizeof(ref) ); // clear it all memcpy( ref.callsign, d, i); - memset( ref.callsign+i, 0, sizeof(ref)-i ); if (i > CALLSIGNLEN_MAX) i = CALLSIGNLEN_MAX; @@ -1609,7 +1647,6 @@ } return 0; } -#endif #if 0 static int filter_process_one_e(struct pbuf_t *pb, struct filter_t *f) @@ -1616,7 +1653,7 @@ { /* e/call1/call1/... Entry station filter - This filter passes all packets with the specified + This filter matches all packets with the specified callsign-SSID(s) immediately following the q construct. This allows filtering based on receiving IGate, etc. Supports * wildcard. @@ -1633,8 +1670,8 @@ return 0; /* Bad Entry-station callsign */ /* entry station address "qA*,addr," */ + memset( &ref, 0, sizeof(ref) ); // clear it all memcpy( ref.callsign, e, i); - memset( ref.callsign+i, 0, sizeof(ref)-i ); return filter_match_on_callsignset(&ref, i, f, MatchWild); } @@ -1644,6 +1681,7 @@ static int filter_process_one_f(struct pbuf_t *pb, struct filter_t *f, historydb_t *historydb) { /* f/call/dist Friend Range filter + This is the same as the range filter except that the center is defined as the last known position of call. @@ -1678,9 +1716,9 @@ } /* find friend's last location packet */ - if (f->h.hist_age < now.tv_sec) { + if (f->h.hist_age < tick.tv_sec) { history = historydb_lookup( historydb, callsign, i ); - f->h.hist_age = now.tv_sec + hist_lookup_interval; + f->h.hist_age = tick.tv_sec + hist_lookup_interval; if (!history) { if (debug) printf("f-filter: no history lookup result (%*s) -> return 0\n", i, callsign ); return 0; /* no lookup result.. */ @@ -1720,10 +1758,34 @@ } #endif -#if 0 +static int filter_process_one_g(struct pbuf_t *pb, struct filter_t *f) +{ + /* g/call1/call2... Group Messaging filter + + Pass all message traffic TO calls call1/call2/... + (* wild card allowed) + + */ + + struct filter_refcallsign_t ref; + int i = pb->dstname_len; + + if (i > CALLSIGNLEN_MAX) i = CALLSIGNLEN_MAX; + + /* source address "addr">... */ + memset( &ref, 0, sizeof(ref) ); // clear it all + memcpy( ref.callsign, pb->dstname, i); + + return filter_match_on_callsignset(&ref, i, f, MatchWild); +} + + + +#if 0 // No M filter implementation, but there is M filter parse producing R filter.. static int filter_process_one_m(struct pbuf_t *pb, struct filter_t *f) { /* m/dist My Range filter + This is the same as the range filter except that the center is defined as the last known position of the logged in client. @@ -1738,40 +1800,21 @@ Caching the historydb_lookup() result will lower CPU power spent on the historydb. + + At Aprx: Implemented using Range filter, and prepared at parse time.. */ - float lat1, lon1, coslat1; float lat2, lon2, coslat2; float r; - history_cell_t *history; - if (!(pb->flags & F_HASPOS)) /* packet with a position.. (msgs with RECEIVER's position) */ return 0; - if (!c->username) /* Should not happen... */ - return 0; - - if (f->h.hist_age < now.tv_sec) { - history = historydb_lookup( c->username, strlen(c->username) ); - f->h.hist_age = now.tv_sec + hist_lookup_interval; - if (!history) return 0; /* no result */ - f->h.u3.numnames = 1; - f->h.f_latN = history->lat; - f->h.f_lonE = history->lon; - f->h.u1.f_coslat = history->coslat; - } - if (!f->h.u3.numnames) return 0; /* cached lookup invalid.. */ - - lat1 = f->h.f_latN; - lon1 = f->h.f_lonE; - coslat1 = f->h.u1.f_coslat; - lat2 = pb->lat; lon2 = pb->lng; coslat2 = pb->cos_lat; - r = maidenhead_km_distance(lat1, coslat1, lon1, lat2, coslat2, lon2); + r = maidenhead_km_distance(myloc_lat, myloc_coslat, myloc_lon, lat2, coslat2, lon2); if (f->h.u2.f_dist < 0.0) { // Test for _outside_ the range if (r > -f->h.u2.f_dist) /* Range is more than given limit */ @@ -1785,6 +1828,7 @@ return 0; } #endif + static int filter_process_one_o(struct pbuf_t *pb, struct filter_t *f) { /* o/obj1/obj2... Object filter @@ -1801,17 +1845,22 @@ int i; // const char *s; - if ( (pb->packettype & (T_OBJECT|T_ITEM)) == 0 ) /* not an Object NOR Item */ + if ( (pb->packettype & (T_OBJECT|T_ITEM)) == 0 ) { /* not an Object NOR Item */ + if (debug) printf("o-filter: packet type not OBJECT nor ITEM\n"); return 0; + } /* parse_aprs() has picked item/object name pointer and length.. */ // s = pb->srcname; i = pb->srcname_len; - if (i < 1) return 0; /* Bad object/item name */ + if (i < 1) { + if (debug) printf("o-filter: object/item name length < 1 at the packet\n"); + return 0; /* Bad object/item name */ + } /* object name */ - memcpy( ref.callsign, pb->info_start+1, i); - memset( ref.callsign+i, 0, sizeof(ref)-i ); + memset( &ref, 0, sizeof(ref) ); // clear it all + memcpy( ref.callsign, pb->srcname, i); // copy the interesting part return filter_match_on_callsignset(&ref, i, f, MatchWild); } @@ -1834,8 +1883,8 @@ if (i > CALLSIGNLEN_MAX) i = CALLSIGNLEN_MAX; /* source address "addr">... */ + memset( &ref, 0, sizeof(ref) ); // clear it all memcpy( ref.callsign, pb->data, i); - memset( ref.callsign+i, 0, sizeof(ref)-i ); return filter_match_on_callsignset(&ref, i, f, MatchPrefix); } @@ -2006,7 +2055,7 @@ /* OK, no overlay... */ if (f->h.u5.lens.len2 != 0) { /* Secondary table symbols */ - if ( symtable != '\\' && + if ( symtable != '/' && memchr(f->h.text+f->h.u5.lens.len2s, symcode, f->h.u5.lens.len2) != NULL ) return f->h.negation ? 2 : 1; } @@ -2016,8 +2065,8 @@ static int filter_process_one_t(struct pbuf_t *pb, struct filter_t *f, historydb_t *historydb) { - /* [-]t/poimntqsu - [-]t/poimntqsu/call/km + /* [-]t/poimntqsu3*c + [-]t/poimntqsu3*c/call/km Type filter Pass all traffic based on packet type. One or more types can be defined at the same time, t/otq @@ -2036,6 +2085,7 @@ t = Telemetry u = User-defined w = Weather + 3 = 3rd party frame Note: The weather type filter also passes positions packets for positionless weather packets. @@ -2100,7 +2150,7 @@ .. 60-100 lookups per second. */ #ifndef DISABLE_IGATE - if (f->h.hist_age < now.tv_sec) { + if (f->h.hist_age < tick.tv_sec) { history = historydb_lookup( historydb, callsign, callsignlen ); /* hlog( LOG_DEBUG, "Type filter with callsign range used! call='%s', range=%.1f position %sfound", @@ -2110,7 +2160,7 @@ if (!history) return 0; /* no lookup result.. */ f->h.u3.numnames = 1; - f->h.hist_age = now.tv_sec + hist_lookup_interval; + f->h.hist_age = tick.tv_sec + hist_lookup_interval; f->h.f_latN = history->lat; f->h.f_lonE = history->lon; f->h.u1.f_coslat = history->coslat; @@ -2168,8 +2218,8 @@ */ /* destination address ">addr," */ + memset( &ref, 0, sizeof(ref) ); // clear it all memcpy( ref.callsign, d, i); - memset( ref.callsign+i, 0, sizeof(ref)-i ); return filter_match_on_callsignset(&ref, i, f, MatchWild); } @@ -2191,12 +2241,12 @@ case 'B': rc = filter_process_one_b(pb, f); break; -#if 0 case 'd': case 'D': rc = filter_process_one_d(pb, f); break; +#if 0 case 'e': case 'E': rc = filter_process_one_e(pb, f); @@ -2209,7 +2259,12 @@ rc = filter_process_one_f(pb, f, historydb); break; #endif -#if 0 + case 'g': + case 'G': + rc = filter_process_one_g(pb, f); + break; + +#if 0 // these are compiled as R filters, no M filters exist internally case 'm': case 'M': rc = filter_process_one_m(pb, f);

n ]^^cznb s7ľyciiiY{3ʕ:q) >x5GmѮ=u\8KjH**\B؜@Nznb8NJl~)`TuOcD %.Ee>h8B2G2Hy-!wݷmQRALY71| `iHAѥA>Zٙ/{ѭgUjd2wds'.zerl܁<¥ '4 Z 7`'&,([ WY9ѐ˂ϟxh\o{*ㅌl@mgjP-h>eTd-?.!!QQQVېAC>~x)">[ѹG}>gc8Բb{ck` &6Z2Ҟ={3@߿}]a->ڽT۳O*?:B7*--ݽv9uk`5 :yyFS &hp)I E픔3? >~XUs.LnռE)6'"**M:]usmwIIOMA4Oh.ATt雔xmUGrnW'H2KwyWh5w[ ^g:a-o)#z Ӡ}*OPx,p' 71\ػl\u5<ˉ>$\z$Yʒ ㌏'wzRoA4Oh.AT,Y}d)))z#:$tYY5M&5(D>.Oܾzû FR>Μ)Ş=uGI UTXAŪf&6CLY  bM W )'_oU:̘ԟL0uIsߪueԸ~6) %ۮ?#lӾ;s6E%eP1kdt5jh.4i߾~xtd?}zy.xx.m$';)dۿk>uۆ/jvz (kߎk &<"صf-`ꃳ0~6%Օ bõ|2uP租{4O!\@@ߤ77679|괆yދdXeψ BhDqQQIq$$&Jt.O SOA{#řp=!\@97*+XYdd1SA=o͞O&E ?xy$W)77bdM ԣVڬYmQӧ\7zTU 9kCD@߈`![Ԉw7`'%4eee.E<<%Q5P SZha'_h؁*!+%O++]8LWBRQuC&6lA7_~4_dӏ4"~r~7`'@pT[nΜ븉<"i ye`V>D{ׯRsN&},m['ń{hF_D˛0wz Kh:H1cR˄Y/B3WMSw$0LG=Ԋ=%4 ?}$[{ׯN;l>+|jHFNwX9'%?bTNZ!wz Kh:|Cǻ,S4ghʼUrN'j#Ap8^={ׯU)O1{DC*6  ,.n|42=sd vo>uds2*$RdgGΕʋ ;TAg9mlm;h+ $ :,(:WH!M- +(7 Kq`bcO֘ݛ^J+ Mӯ(/WRQb1Ԃ_TVh .#~#cOUu&%vE&FڲZpI&֙{[{ KBɰ1  _V+߅^[ff=05P0< #}0(!ot/Rߓh L9![gp6ƴ`?2![{ 6ƴHbUx*2h̯q`eh톻0J#{XHm[y*ґ=Gv 3Y &ڢh>5qn*cOޱ'݂ld>z*޾)u!ꎅi+r?֞&GQTP%:5|Lr[H<ʄ0#VSNI+{GIV0wPH:Na^l ruyRRxL )l`QCMZf )j.*C4 T0} W4 jY蒭BNq&G94֋9W|I[Eh9[t{$i{` ɶ~$ XRݛ)&zl8J&тCVkas>r0SG`1) xQa;\H*?s Cȋ;?M;6 V %FL Ig.r+XWx VxzR<$>qR foP$X0t[XӲTSQ,m|tTХ$ȹ"eRMg) >b@xx 1kػ? ̖eBjqt\8G+"}kݘMB$I$Y S*ԧGTo;qFsFE0$.OpGf'Da?뮥r::1>nx W9&ޕp_wQ *EƯ\TĮtBUk7O73 *L 硛C3,Ѽk.њ.Mj1fiX1T]~HvU\QBwӂ <41lX*T'qU؁$G%5alffyæwh`q@!y J @AlEvR Ht Hɟד#A*j''PqZn&G:֟{DH3=bSFyFdޕ3v3" HV)D_ׅ-HFd,^FY{vJ"Ш9qIDq%p?[*8% oJOeHG%FDs ؁T(a9&R[JCn5̱Fv_o\Qu^/&Tmx}98)W㦠+*PPgQdRqS9.x\!V8O,DzǢo௑;O3U̕4q4&C2JPDR*#<*4 ZN2Z7xz'{Ҟk@9'4X4tK2LG,H$֐؉[6֮y͐Z2TX۲ 7m)zZD(_ٱ F%J°O!lB _( Wrwf-hâ;h:Z}'nـ^YR>nφidO1bEg:2*0ӪBz Eg. #L(,%;ݐ34QpX u;x =^qp 8]!B}ϔ֏+c,E6%H GA@Vب7@ w)'Ʈ;Rl G UjMsVmS}9Mת=kS֭@ޭ!EB\+=IB J++)D\ɖ{6Mz:zrgxXST [(FD1a u?IIL}&?\K)*U^.e&G6*eRl}q{8=,Z?K8]p%nSYР==<>}* `<6|9;o5hǴ];֎(-+];wF|:#8;AE#80g)b+*;>k1\oiuo?.S u胺+B{ʼn'`{rk*l εBWy8 UyR!hGpqat$z DM| MM/#z#n Ť k1z\y~j6Zt#Ѭ`o֋7rK6'Z `>\C,MɕrxOI cōА=doj|(ԪAGHS&zк3O,V ?(Ѝ\ !%:ܮB>RMfSNu\#0+?gÅVqvO5Hb^q *i={Ff]jyM2E%ڪ24߮u 4:`g~Xs,*g8u6*ϮFrYV'7p8u*繭OFwZUlí49qꓓQ&V$[3Pg- >[EUMժz9nigȬť\e29w2yz2~j9c zX a\ukSw`u4cg8;Ӕ5H+\x2Bsɚgjp)Y7ɀuປ*k8LM: 5 b:Ҋbe#K~7dH+Z!&םi˶^.`qU*x͋uԊ!hȒ/Bh /|- *uP7WGJՠB~@+F3yQ(S+m+G3=yQ0s/7]$N{_Fm:)EaN$'n0397:ɯLTmY{iQ?tc0vwt -gΎ:pv4i#|OoBY}G0%}RRK?ee@\8g XMUyu aɁlթj¬~L8RXX EA.iB}+p͜_{}}\7=]^_ddx; cOYu/0>Ca|0OQUgo큄 a: Y̔\fFG,~ּ}Lqq\HHn6|y\]9gF$f, Bx@.PGi.߆&G'=ր\+FL!7_5=Ua@em XoJ(!€.t) +WVFkFKlȨ~_6œCRϔ/z[v92HÓ8= [odI\+9)HlZT~}|ԟEъNj'vd$LiG0Tk'TϦg ?M co7EVKG8КoAei% 3nmh#LR y!+d,Jjg?YWy,wb7|j`9jI0nFP~yB<+Ϻ$o ϺD_{ ' ~2$}e~e80˶3`<jyZm ,_^J2ugbW~H5.3XJ =+yBS,20!K+ROM 7O'KGvpZ.+fUSgqϚt4ջV˙꓎`05eJ#|% ?PK,,7PKaxD manifest.rdf͓n0w236Х@U1*wu2TUĐο;y?R5XJ2ҶK龎vʷ@@髊eYĬyVOsW$@L눐(NOO-> I7NߨJ`VNʄLvg,B>nGeoc -^sHZyGE\>Qw%m}Ѓj[ý?-PKZPKaxDConfigurations2/floater/PKaxD'Configurations2/accelerator/current.xmlPKPKaxDConfigurations2/toolpanel/PKaxDConfigurations2/progressbar/PKaxDConfigurations2/menubar/PKaxDConfigurations2/toolbar/PKaxDConfigurations2/statusbar/PKaxDConfigurations2/popupmenu/PKaxDConfigurations2/images/Bitmaps/PKaxDMETA-INF/manifest.xmln }ƀV*=@1N0XTۯ&UUMͅаxz+ #0Rm^`*Qqғ0)fjjMMKuUrgtO.`09*!yН7ٺR u kN5/ \ ЦQי7U8CCbxUQ!-t'1lJ [AI[]({vitWuۃZ\ѻ;#[Ax?#$mw=ϦQ .~'aV#hYNhW(cQ9&$hYG:!5n ~|Dq$?u?k)cgk0#ת[o.,1.yj7ߝX7~g֫>Ћ]z _PK#PKaxD^2 ''mimetypePKaxD\&MThumbnails/thumbnail.pngPKaxD 3Ug 3layout-cachePKaxD ɻ` content.xmlPKaxDTA'O) settings.xmlPKaxDJN7meta.xmlPKaxD aa-Pictures/100000000000037400000121B346EC85.pngPKaxD1u1u-=Pictures/1000000000000265000002240C232FD2.pngPKaxDB{7+7+-Pictures/100000000000032700000061AAB92425.pngPKaxD  ))-Pictures/100000000000033100000061319A1C8C.pngPKaxDMHUU-Pictures/10000000000001CE0000004E051708F7.pngPKaxDƶR2R2-5#Pictures/100000000000034E000000B73AB35E75.pngPKaxD;,> -UPictures/1000000000000374000002304F2FF377.pngPKaxD,,7 'styles.xmlPKaxDZ manifest.rdfPKaxDConfigurations2/floater/PKaxD'Configurations2/accelerator/current.xmlPKaxDUConfigurations2/toolpanel/PKaxDConfigurations2/progressbar/PKaxDConfigurations2/menubar/PKaxDConfigurations2/toolbar/PKaxD3Configurations2/statusbar/PKaxDkConfigurations2/popupmenu/PKaxDConfigurations2/images/Bitmaps/PKaxD#META-INF/manifest.xmlPK'aprx-2.08.svn593/doc/aprx-manual-pics.odp0000644000175000017500000003054412016465526017124 0ustar colincolinPKA{A3&//mimetypeapplication/vnd.oasis.opendocument.presentationPKA{A&ڦmeta.xml Matti Aarnio2012-07-29T01:00:05PT31M37S132012-08-04T18:26:03Matti AarnioLibreOffice/3.4$Unix LibreOffice_project/340m1$Build-502PKA{A settings.xmlZ]s6}x>>q&pHhMh }bZ$ǡ1)Cw:CBl霕v=KAHԏkp=8W.p6.4=tHPJGz: D"lrl*!մfB(j9sfq|r¯///PPD%#\N"Q?/r|D|5LX.,1QVFˀ\fl 6Oٛ"yaحU/o ZՌqR箅Tig6t3*TA`b(lr4aBmNּsQi6#L0tV7"7)WNrR;n|{3 ~q0oS,Z0-Pi~~Qů$P x\g;+S,X 0S*#im[_2ݏ )"0 E} ǜ4, _)员CʡDા&~v.G,{ ddQbAce8рr`llP_PCu*j5lsCioH8?j#iWs\lR5Ct{ߔPwQ;!mC@nQED9Gg Lil`K 5I,|EӫK,㼿H;Ǩ4uѝ)MBЛ.˖6k̨rl[JM-c=Afd&ge\Pmy&v0Ɩj_7~AҰ=$BNg 0gx1VC=] E1w,W:LF,^n1#Gz͸uu;mdd򷬖":a5QK^ĄpG_ǟ"m)AU)XԎUA= Lt'r5+sH[sz0#SvD)&QLu_~P(H #ÿqlNd(VSn[7;9~"`!|*- rE[\gwnjqWEev􀺈O AtTFα^DrKtAbQeYߧjR*I1(kpdu Hfw|QaBHZ&Mli}"x٦8T_WWSZ$X,?GZqeeFQ=E:u ,1}-Pd}:0YdY2˯R1hS{1yo+y,zmgivnz:L ٞ2?)ӫ[TwPKl!,$PKA{A content.xml]͎HS F;7?]5hWZiN m4Kqod#*(+P=#E׍t^h u;ԳͼCW_̢S[+zbq/;0CwxSNSTLCk}ꥣyi4W|G[8?:`I[Kfg퀬 9;x#eAOBaޏ2 i^յ`'I/z1let*p"*QDOW^JҐOYVECBRҪP$\i^SEmzY$Am;bMŴc].w ?_*p%i ^fL9XkhZ_ו뀅4ȑ[qL=$4{@i&GqF,죯/O֒dGN+!v.sjkh-XmSHc8*A/>LAbd2Zs܅ɧ)"탤wDyatSp:Ud0#WjHD? *ZOdQ'i"2i#^qlOsgInN?osfxH{"R8,r!}L?^vB.R"/?Ŏ?ۉ_2е}ܘ0-܊P' MGo*>!S8 fʳ40X E%̈́-<]I]z*Ή#R%dn:#Q+fI}beX"(" Ym2n.xt6 L4ɐTe'H![!o{/r#s8-P=Al SڒO665 |Ι\K4Pu?t?owf M4մ|C)3mDzT0,-mfS卑y !a4;j˼nVT{i+#JS-ZTrxLH>R8 xsgXڨo漥Rzvh|vHL0'p vrƗ#E⥇KrLFc8dO23-ze,`;?mwR)wAV"Ē3K ?k$! r>B7 }2/&!M[)%4n[)$dR>ɧ~z|1 ['P;]LDrԹI;2K̸.Sd%` k=Aeyed/f$< /bjl_'>џn…"ħ幣M [-G4QZ*Iff77zO/ACUH'V'S&ш_MP`w;g-e6V4 2-7fxNs%q0:ʳܖ͂9e5Lc QV7NCԯidiM7ߟ=+ZQ]q#{o4mPE8"8 3OdbbjE$vq$.92c2xFVKpf106:ō-Ee$ZӷpzA`vNK6vjh8ﺼ{"ICVq@fiv#g% MLIїH NOBC~H[ΆprgaΡOׇ Q~Vn+}}rqs4h<dy ?`^H5F%0w`RDSɰCǟ22q!-Rdڍ eR<m1-b[ 4+߇X֭__)!6-Dm= =.{{`zWٚ*0FmOZo@[ەؕͬғ>D;;6{aBS&6bCG 66U3P2bCa{Hl(ĆBl(Ɣ7^^#bcXC| -#аִКZXAk>~`Nͭ\7pﵹ\.f#_F*{+ X9m&iCVNP<`+X9ǩU,6X|zej,S7$˄rI&R7:ՄUF}-q:VJUj&'Jp ƊI,7aɿ18lwlʝEb.PB>/y'`5#xa 5Rv\`qА$vE ;Ž;'vQ`vGA?aZ٫;i6-~X1]ʣзro8'JvPh %X(yMdWJaȼ(N l<\{|>g-Cvwgln\ R,PKׇK hPKA{A711Thumbnails/thumbnail.pngPNG  IHDRd*1IDATxAr6@ay&W z.zk랠" zSN9E ?oIdEBL>DM6zH 4fB3 @h 4fB3 @h 4fB3 @h 4fB3@}}{pD<@ 47q0Ʋ7=ܲ6?98~/wKk?-xH,/U,';q7oooCq <|Wh 4f XfB3 ЄŇ)' ք ),o0d0?$t6ܹl2ׇ 'UQkNo,zX*^U[=驪wھQY bikcJ嫡_bE?Q ҵ>Qe}󫁷{^yO81r B84koW%)_b Ocf?9]1Fϭv`Q_G./Rl{89YxX\C&cf? /zUeqz[ugl{8|=CGnCphFfNp,zl<#Anw ^~oڃpGP@?q\9˴is{b[Ɨ+&ϒf$ԇh+bȡ'f!gcΝe"3'տY?wNϨj@enͿ8|{8-Ӻ ,(Ž}zLsݒٌ6@B3X 4j_L8 @h 4:wpmP%\SiµA!PӮ '!Rο2.@rAr_Z7ئuO&/wi`egϾw#T$+:$pWyꛏMDP<_UV'nv?3@rG&/{-z3Q#;3[FXزgXnOeMFۅ׃uַcs 5Y#}n_-G! ~8^_.e*k0&K#:d{O_.L=A%VkeM*;p( `fئeB3ޘ M˕ᆙve8W@z"0@Q3 @h 4fB3 @h 4fB3 @h 4fB3 @h 4fB3 @h 4fB3 @h 4fB3 @h 4fB3 @h 4fB3 @h 4fB3 @h 4fB3 @h 4fB ?:9RSIENDB`PKA{AConfigurations2/images/Bitmaps/PKA{AConfigurations2/popupmenu/PKA{AConfigurations2/toolpanel/PKA{AConfigurations2/statusbar/PKA{AConfigurations2/progressbar/PKA{AConfigurations2/toolbar/PKA{AConfigurations2/menubar/PKA{AConfigurations2/floater/PKA{A'Configurations2/accelerator/current.xmlPKPKA{A styles.xml]K60$7:d7X` ;-63(Hr{NKQ%[m=A,UbD}ea +f(#zG^L/bS^ԄFI@ZCOiKSk%1p,]ҋUM+I ;N3jЬ"0z #moc)M>}Nx]u*]V'iEU}0W{8ƤCbC("/12F1 ^N$eIVM>p:e @^Y Z!Oo5qZL1 rfO<.P5Ncew[(GІXwLE BW"MsRD+dd& t{%A눎$' T1d##/5t5S]c<>t.8!(!UuꐰPFAsPqzrr*WIMUx3$xP O OL'  8!$+1+PD(1/J7D)+ @܀%)!+&Lc_$&-$2~DZDAL2EݷT5$ M礵$2+/E^ON _hlyIR nsp|E[SA3x8yC͢M@@mCsZ[՘\zj<Į+od@@,Z5sh&J|Q֘2#UqRg. _J>sxY*dI`(VVV PR\ItH& /pbu x%-'K*\ᤇH:ڬQ`Nj:[oH@bqt?Y$ip`K:߆C !!&97Ф7g>7jg!ܨv{D6i}j:۱u( vT`ȶYAK.XoPb YA4Y)Ch 'ŴnRCIi>Mbo+5; qX0l~C#, 2{ <Ės%!@, S%7>S#g^_/i V+7y  hI'O[ccѕ"q~4s\O2ؿxQBo4M#M|ln[™١g o:BrbimD?9r~pX݇z5(ݙMa  s6̚md^Q1vA;-&+|_޲>CӱuˁTڋ~wȬ\-tNETd{" 瘯Mg/[˪,v5 N &Di/m;FV[=2e7ab⥕$$ڤ/=,8΃{Ǧ-cMGlFΛM ɒڣ}45Y%6? +fijcQ&RJr)n?1yý3cׁZԄ{ա$ctj[z֡Y&7D_FSKsoLZ fٙ251o5;J=CEŮt8,21]iv́B&bV?Rk,{,bN? fu@l˖Ù"61ĺ_ I?Ħ 6~AwXW&~ ?Ӗ|?-O{rv' w# w# w#\p7p7p7p7p7•ڼ܍?=hH xg8ͯx=@H C a$\/BC a$ !0} ":p%!Rmj休@W#ՍXoxhN`Q@ DG ~do(x;ߠ㙸n -~g4x" Yvx&1ߦsa& i]K0if^ЛSf>ܵl1RؘT73{֊&ȶVXN 穢Bx7ՠ~4 #Ŋm]XSrĚj. D;v1@rY `^JF2DYr,яJ7(dejC"b޻v2qc})E' SG *kwo% J|4s+ };]/w[充ߡ0z΍fӂ;,nQ;$u oMTֈI밮EOũ_]/΍ťgDPJ&0P]@7?5l nW;G%]K(SI(kSǏgčD!`H4D!`H4 .Аh0$O4hv!qwz*k̖b }c"o~=q A>ԂFՒz h#gy\*P,z $^B1IQ K[ m'Jhr^ndU ^ Ƃsds0fY-@aȕGbw=; OO, ybKXΰݼ֥4՚hcX-?z[u}ԷWf`޵7uSi,=#]\gщr'r@e05ή2?oǦ93"=ϴmg/~gԽ^'s(wU"4y5fWcIh5Ldhyiri)8KO=#["ᄉAjGenkz"TI1qye/Y,8ZLW3\#{ 6j50B~ؑwZiehF߽CV}+qK]Nf"T=jsǠ嚑}<S͚%-2m-Y4sJffUg\eɸ؎bG:@zAb!w58?J;(Y/$PK2i CЮPKA{AMETA-INF/manifest.xmlSAn wCSP/H@Aſ/'m]rbvgfhggdLKڲQzL$IZy ɯrfZ[TWXK:jL`VT ;>KP]B>UDz7Ci+`> cDX8l%OFOPKPKA{A3&//mimetypePKA{A&ڦUmeta.xmlPKA{Al!,$ settings.xmlPKA{AׇK h  content.xmlPKA{A711Thumbnails/thumbnail.pngPKA{AjConfigurations2/images/Bitmaps/PKA{AConfigurations2/popupmenu/PKA{AConfigurations2/toolpanel/PKA{AConfigurations2/statusbar/PKA{AOConfigurations2/progressbar/PKA{AConfigurations2/toolbar/PKA{AConfigurations2/menubar/PKA{AConfigurations2/floater/PKA{A'+Configurations2/accelerator/current.xmlPKA{A2i CЮ styles.xmlPKA{A+META-INF/manifest.xmlPK6-aprx-2.08.svn593/doc/aprx-requirement-specification.pdf0000644000175000017500000052622012005774516022061 0ustar colincolin%PDF-1.4 %äüöß 2 0 obj <> stream x]Ko6W\.-> t/ )]GLb,زf8pe{O?j:%7?ퟳO'c1F55?^xE/ό3䠟v3Ӄxf-3Î?zW_Φ}f/)g?p%GSc=sOT|^}WܻG66Er(7č/΄[E.MCwOO/O_#)v/Gxl*t`K6ፓkԼLeJVpG f +}+X4"(0$v9[EĎK8& G4!pgWr rOl>{Mee}״[7z0h=CK$"̃ rI!9bTlz1" xZij.lar)2wF+\IRn>5 H_-o:Q9Ci{L쉻p5BAa)cFHKb>.Cd%\i>S3y䃀Z"`bTA/# t]xk<&le vxHVӄuup'1MݶOeo[vn%JB)vJyqߤWߗs[O#eAы,"K^\ujG?@gOذ^|WPMzRɦR@ b)7gKU_t2o@kwuLfle> _ gJ3t}gu 3^-2b3NԔKXy endstream endobj 3 0 obj 2581 endobj 5 0 obj <> stream xY#7 WL# 0ؾE XnM~?"53ݤΈ-rI6سQӏoӯ?Mbp'}SNRM_/B %b/,'vQ1 Ex#, KV|zw?|| R5WkHZ;/"5>E$Y؛J6(JyI!mUJPLIԹ;U^g,.=xF>ぞ 2DXɞ^;䴈'Ajșwp#c@-'sj ɀy" _݂d)ĘO':"_URqǯsW1dA`KR+ ʉeJb+> M00l7!QZIGtTӪ4: HEb!%RbK}]19'G?2l.I֢Js8<h 1buꎐEZDYQBPGVq|0Ga)%C[DkTρ'%Xg>08-j0, g춎c m*ա4q,5R$s6+C{h沟i Y>*pH6הJvIJfPHF#Z !.F-A6;YσTf#f &\l9r@: QQdf$k{}̒}cpr6&F[q F yWmkUVמ T=R5X6P R5HGu1l*Wj2zp+F@aEސM0ЍiWsWw\ռk.T'P*+6KD(lT1[EfNUk?>e/i-QD&[ޙk덪īMdP#ͨzCZ%Rw(J7& gV8bB>3rǘ]ܛ;{lfBQ2S4gkƠ-q% ua@즕fOZ' )M\}vچX2lF?NUh`tA Yyq(SߝW2F'v a (eg=I1i"0ذ@?#"zO*r)z?ΐ@U09ai&"DPF&b"z>ُhYE*`"ݨݸ0Gި}8F#XLC endstream endobj 6 0 obj 1793 endobj 8 0 obj <> stream x[I+_Aܳ AKwm sr,5`мVUDfŒ!&/ϋݾf_/{qo?_?/._ӻB }ܮۻ7.|[c]ӏ=}]gg"}HL?Uzg6u]m*MkJSW*m0ې3eC-H,? x{~|~ݛRup;!pSbon#0,IRQ"oDG|{.^pX]Ɍ?d``{@ٸ-T>tPHZIIq?R39|ć:I$DO*rH%A/ kڏ^i/jbç,R ~+eqxyB Rq qRGA.0@̈́Vˬ!Fm0X5 iXXwYHYqєj"?+cvQ@PAmWOE>>nG/`-Qϑ(h?Lj :8ʿՁ߳B*Eَ'0D9Pcȕb, (H]<ȯ%mexݝъbGzP_fQ>!]UHF^ULI"3G'KϏBa>,v$kl[ekfHlc ĻQ+ٙO;`z>\R–ˡdPMf 2jX*w뿺り$xiamrPZS9zIpcv"/āqI\xYƓ6?DA#T#" Nd^1șEY>;3hQo" -{̺=]MɴeZєuѨM=ԓj;pYHRmj8wJuݽK^u'2ؔ{&䌥BYX 1G(c]k2W[_y$@&J}~ĸ@@ M_{•lWK ^ )ɪAZ²RrqP?C_7(t_bT>GqG9f93ԝ\% W yTY E28% VρyҦ%GY˖J]UK4uG-F M0˦-E ƃOBy(zHmJlp$|=׮F6/BgYp,IO̾jg/cOJٽ2#$Ocq`;4̳WS^I1V75WH$~PY֢&y랳\F1x&/uKOMg5,o nשlM@)mu+"G_ 2^ab,D?[.u-۠360xߏ\ xs¨5RX5f3ܙ%T̀~ Ipj ML A^[zxHtM\dS69x }VRrZohgeiY;\V`<"]wnۅWtc=o$:[5xû9ņgo Hy=ӕrH8Z]5fxjA֗:Rgr5"ޕy}BHPy}豹qxiL{:. ]mo\i(. Bo y, <׋}!;ɦmeRtYwδ߾8)lNAwac? 9j̖nC \wوaIBkt[ -idFz8"ٔB;J  *ym%2f68Wc&ƾ ؞g6řtJm3\‚gDh\-<ඖ!.UeKfx`Ms{ʼn!x$F[}6aժ'$xuVε(q`vhB l/j9e ˢtvHDzY`ͤЭ[ w!Si-PSA3 <c9 S̾f Т)7"+T=*&% rڅ)|y6WQZB}h {p }iiߕdF+7/ L Snn*HP1PH&ɽw]wt&[O5JOCc|M3 ̊~$GI72@$# 9x&Aɶ$@/ xؙHt0 \'% OR4KØ,5R@< R0$KM2,5wDb֚B Yj(9ެ5WU@<5`yhYR桷$KC $KS KC9J$vy]dyrdyq@ԼR_.^$ endstream endobj 9 0 obj 3340 endobj 11 0 obj <> stream xˊ,mׁU04LwO/p ]nYr7ؒ,ɮ*W9I==U,-Y3˿2埰7叿O[Jo|/s1zd'w ])ti.,k~QczoXnʧ^᫅5K 7w)`5`xxG #d kFi J"aK;(f.۟ottK|4q%n3dl!Y@j|XfaY>>Sbؚ=!͗l> Ch4Ϡ"BCyZ7ll~HQ8=3e,vk7NOqdXT}!/jZ `2Z0d, ] NgQ$4Gلv kecd PWeM 6 sg%ߥ/e,, uG(_~GidB({|)zEp tn[^ȬKlP,@T) _aFF=p~f~b:Oܔ FD]A/2YwQ>6. 8b5GQ'MFg״8!'L2YK&` \S=ij/|(mК,A6PTM /,EǐIXB*__ѻFanjF ̰z)ڍBoiCH }[.S5 FÕhA -K#C8n-u6+'cG{H8|z _2@h qbMHA8{-ޫ?'E WPǟ! bV{YQPoagyT*1j3G؈ PWZΕ(!x ~"}-N3 WO)sI)d,JG\l1_*ekljm7}+݆!s"ܤ䪢GOE,Z] W+@A-m/QpC&9e{oH$}'u l$e" T(ѼGJdϣZWY`-]rjsMhUZ)uX;Vκ[c)z'`oUC,p\c~]@}:8vПx͂7^Yi^2έ BfaHpe9![iHԾҐf| ɹv~RjH 7#R|(/2NQM}Y5sjʵW# n|e293 ]es I[ ><)=/@ qٳqZlԵe4kr";V៲g9sJ Uڰ+'VsZ%C[&KqJ<n!N )U7Ym̗ϦUm6(fGqX Sq1f.3pcH=fIruP>xmEqX}jE-:H7i݅:ޮ2չuʔ[}7^+it pbע\#A煞8hʂR'[6٣QΉݣ tQV>hxɮUL7pq4❂PUb.)\1\]%Q]{@oxY?X0od W4 }`ˍJTE0S&Ss턽ch.e߸PiQB[5 *ʑF"ih30}XZ] [ӹZGT*C;oȑ[ROM&19 :٧e̤5>cƞ_fgŗvtW:n\wΚK΅5̗}MB*‹bħX{R=TaDI6o>wi֭hg{,!gtWc0"}i~礷Y'+تqv"/t=ʊ[xa; "#=C?9$i/jPT *uef.dRbfMQ [d&2 q}ecڮZ"nԵdMK U74L m&Dcmv?۫5Q wϒ6 ZO'oU.#W~֍R<# #T1fj x*ip:I 6S[OaM`߸2 ]UAJ0]]e{ 8lePl\RjIo_q=Q\51Mʦ8ADKo gMځ*4/LoWQ ׺eѳsJ,BMbq Y> stream x[M4 ϯs;,WA36a)B6ߏ-ٖd\yg,HGK]׏T7qՙ/8w??!\)w_J+_׫7sU 5=5yӧz\'ASI 'bT|l^~`U\ tlNzh)HX#F"?&הha5:?swE+>!#&vR#C8|D{6:rq&j"bQh`Z,#*>_kp=%N61h?hg (II 9 Hifa+hv$̀%:Dr<6p?8äL 5f3%_sW^G BDO h˦4 ˛ mm">f"9Gl ]ZiRg+4`0U U^ յAr| .SC ©S{$z x3($(c>6g=Od|O҄h^jŽ%zK evڙcZݸyEL $_k Ma,AtPHhђ`yVkj/-y K7]"S+YBʹLfݧe&+t M9*nƣ*,V&d)3xfֽ̉MyAUc|H%Sw|QJ.G}I ,z< Z[ݾ#d݅2W y/b!Rx~JS"*z$RǴ2R68K-q)y/lG*(fP&>TcBZkTTm1rw[N~ [AQ$OIoo#gh7ϺMu~=Xk-a&O3Z0t_ps4È'Idx+bwi m=˵a饌-\CPvPWR{IapnM)V..-WqSeu#y#[*ƒp۔\Nk蚦*th_z]J{ t5%grD*~eVauB)4،u%D ȗ-q5hnlm$PڰUi&O4v]QzT=j53QIM$zn_:Fpi ZSS'f1NDb;t)+d䤥!JJody55夽67>.Y&MLwK`JolT*"f:KsbQ_Wĸ \}.EĒ+Y9ent2#j2ōsN˩񱗣M͎uf)os{z8E\{H\ m#6>8ͷ"v8loob t}';cK;8ΨؽBͩ I%C> stream xYK6 ﯘsa~;!0=VXPh/%ٖ8ٶ,Ė,˟ϊo^Ter/Aޞo1%Fw퇾hsǻ(;ru+? *W}WSk})Oxv~OZfj`N*yk OAv$[Ư e)03Y.*EGR8kQ&6vwoo߯F67g&ܝ90pTeM*>Q gPVT\\či2QȄՈ1wl{5fOn-% E:tj|V?7k,5{,+3 fƧ?F\'h ܳVȦZR^*%a`U+IJG/wMWK" +\6XF#tK+wG FkS+!8t%&zO.aB{@0=% X{,Ý D ˪sHݍ,=ˏ|--wj,1`Bؒ08:3%|埱ue,8a8l0tO땢P nwFl)_,OvNf=^?8-1SrCۣ !BNhB:WLFxկjoVZg=XUV/^^nN oK%`ȩ-[:0wbAUDBmʊ($߲fq_p qIm캗Z1!N׺wɫxYZMbQ^9q>mnU5]dT82Zx799 ^0t8:e0͂NVd)YHW&IDW`1O;B'$|[jyW#\-l>)9 ]%'fuM p-{¢z޼\KDA>`+ZWU4^1΄K gMsoP&A~CC(uk0p8 *S =]MKP;l }@߇@QW)z saV62FvUփőrBB7pЛ1HFnyegj[O=eQPFszQ^Reە0)3flBBy3#Pp3Z8;ɣSTƪb)☡L['(EXj.Ymp6/@|F2KLc/: +]D6fU OZl.mYֳK##+&2؈n)˨#lq]K<}Qų_}[% $K5>AĝCbI~VKmySi `rn*9̓[~+veUdJ/tFGQ hLYWB=(%׊YfYP{R.y08uE[,ǥmOiS|ܱ!=;)KqcV5̅eSARW'@~7rhmIe,uJB-rBM:>2B/P !w(dW f GB>]C~B`W>!tF?FwB CDv*:D˔B,[ CD8v endstream endobj 21 0 obj 2183 endobj 23 0 obj <> stream xZI+9_== C9]>̭`f94t_ֈ/L5.[bbQ(%eǿ.:\Ϗn|ח(}ZbcXWvƧi#3S?w|WTKsD~(GVڴwlx+C*WKl(W%UZP {% \_?~4i6Tqc5 q5? 7fm7^t7x08ٺK0:JeO˞.qt@#&yFkb+lR mi8Ij&Mqc~_W7uF̨8utad0 d=-#[VI/6=FLfeRԅe@o]u/[ n %t<Wb0urѧakxt}H^Xʷ'w?5Yoa{Z0zb=k)렅(_hf+ܑjiu8FyKOxLK IKE^j]X$)L*gM둡g,\ЇƤXCD 7̂@T>O9gN:Q Ҍ.N tw ZmȹZAs,sUkv#>RUz@\ޕWN|ĥmaF^= QLP J3*Lw/~o߂2Ϧ+!7U"`o))Fr]bՃ?IsymQ9:jliQՈwP KL],9r5R9Cʆ[jPPsUuSeqp̦1].Q,O$cWŕ2Z8Ȝ1j )oBIѐmS6g1/p\Mł_BiF9d[@.y^k(8%Μo߆^?qhGn)WT~Zj}zAp:\[iH֐ǸC08/h;9B)JM} ACNfx mc@( }S7p̖ /JEHK)TAOÓ \ݍ#&u¸Xe+ !q; lP2G,td3dZ`P cr ^aV(ܐCf @'ZS<b ޥv~C=b0'O\#֪4nsH=4t,ũL,Lf@;u,&b 2G\*ʢ/CɬM7\jKP}KG挮gi,cG]  ]kC8:ѱsZ)ߝd<ԀwRv(؄}6ZfiQU7TSK:tkX6,EǪF~I.!6 79l(lk;.9?s@G*5hiW= S9l7H5lט|dk]0_vv^f`˱Ӄ]hFt㸅3j4e0ř;?NXߘCH睬c`\bR48WHYUpWl~ I\z$ b.ޠ"aqW7Fd91cΒ䂋ʙ0<58Uu v' -9N/pmM'*3,!:X߱\\^J ?~0~  t#+4[֭yΑpI_'t8}SO{bXr-XvBm{#si6:C\o^Gh́)JweNZwXL+p=k-VEQ|>tk'z5=67=Q8݌ Hkaݶ;jލE-֎qS/T mF/HMuAD|/̔] ;%3AtBw'MOQq'ZM@JA2Et(A4E$ pDS4ЙBL A4EhhH͂hтhӂd5_.;1 endstream endobj 24 0 obj 2662 endobj 26 0 obj <> stream xZI+7ϯs`r r9.Q-*zgBt<*TEϜߓ)OaZ67ӷ?^/)R>~ɺg%M.g/lf.l#_^7rŷ7sPy-e>w|5ř1$#;_geami/UBx2PFY0%xKmu8|[Էۉ_>~xyxqeI1UU\=by6FڈzOb7aYwߐ"Δ2%Npb7@-*ܑAQW𰗢CL9tZI61b|1(7ǽ& kBbSZ2s vVj4\7Eet'`!D0hzD[W1z=:>ؖ)_.$pv>j-0 e*WfJ%\aKVXp6 2EbQ7Ժ3~9͡b`i&5XtX L!uF+|IО(g:4xKg 64Im "7sh W=Bv{kσ:L`$`"ABQq@; uc\@t҅mf?Ys~6U[Ƃ“ݿ i;7$ b8a܅S&G:~٠>}˩7V=6{L&Y)*dzʗ(7AObZ޿PM'1(d)`-8F;Łz27O֠'Z.>4NčD%Y'B Cvy  Nh նs'rx(:ϝ2R]N(ZI B#VwBC4H1׉ ѠʴNhUtBC4$ANh Q ;!fhhq6 endstream endobj 27 0 obj 2194 endobj 29 0 obj <> stream xYK6ϯ9޲ivr[!M @TRd[aavƮ*㫇"Dgi1j}|.=>߼{3}1!'? )7{6/Wao*Y^Œ9'<]R}];jIj=r$Qa9MԢ,)CtX:/ !=U/ɉ]#6D (5?4dc kQY>c%~ 1KQ5ݚ 2G>&̅e9`,{ 1 tpT.&hi\.ٯѽ F QJ$a)2il0V1$QcO/ٯ,x+^d=j\VH+פws όVUrum1sU/(l?p~ljfIhEHQ%7% )JdcLd\@ Z[O }U&:7JhXt}0$x:2$kI|dO ;A)3gn|ݸx̃*| gbW`YQcVu2#䢡\!e1fq3fޥۮ~*כ5M3hEY 4up]ӓ>N` gtANNoCϝ>ʺr =38Zv=c7ܡ+yæU-0ئ7sXzSqlMI$+,'X8~mr kgC$0QhUG0SE+Դjİa6sV&TXA gE6;pho5h+q833JlGCgCųGZ9ʓ8HlqS̭̒YӘ+H`2|֫x<9[ˀr.zS~ϪFaUd|muVv mOh4JQ-_5'ŽDhE'Q(|dәdkch2U8c)MV4mOՅM'?jg,jfI;c`\Y-c.@HCUdPǘuu"eEmXa! .v'eTj%jiqܿT6|y&YîtXmK_ xUkv,;WZHtXuyk*ג6[Z 9fՊh2cPwŪ+-l7:Rw8{]. 4xSmqKj (9(jTjߞّ|Pyy`y뭢*ÕhdތdŲA9(ꦔͺTQNzT@:&zb#M Q>g6[}y5ͫ# wnNOIPVû[L0Y)\LRuM6ݡ娗7&5꩓6NJ7H!}Ou(>ŨCY3{,>LcVT;GU9m ~/{#e,"os6i9<wY:wUtUB^`5K_)hR"$0׆-.N5 @@#p˯ՋeK2)S19Beah+f0 (pPqS'w7!cTYZ0t2ޕa$G{>2c#!aRC&0!m&3dxtH,7d`GLl'0-C&e4LaD $hwe6d7LC49aMQ  6LC4*ܰ *q4D1{h ߦ' endstream endobj 30 0 obj 2078 endobj 32 0 obj <> stream x}TM0 W\H*` 3IBeo-P^+2zOҳg?9gdMl?$<@ puL|b@\G޽HU3,P[GK V"{ De%iLWPSA^eEc%w[bI%SP%wE%Pʋ{^i6pa)*Jy1j:ɜS8K67 9"fUD|hÔLĝ(1txA-lTtXP4MY% Je %xtLPTъáK(.4z@Ѱo{m4fv:g6HwZ1r% p|Vt_\WHy|qbePl1'6v_Zǧ̐ǃ|8u'p`׌ endstream endobj 33 0 obj 467 endobj 35 0 obj <> stream xɊ$>_QgCEAWU >߼ߒbՒʞ f{&S!br̛ǿ.&{| -˯wss?_?_5\rXw۟ng=L_ww3YW{3[~yɣ<}W]5|ݗ(O?OkgyamٹՕ5{eݬ/Oˮ6 l>jãU26_Ӗ6o(>ۭr:enSbN?>~P˛>jwHʊ'`{ P*"ΤznR6v`-C-fnÅl/>1${)b!g+eoI g*xo玶=p3f.H{G~ZֹԗovsE؞*y3_=eU.E$үX~YfNT{o9YY\9cM[nJHYP%uOW֕,`$Ű"[ 96$-"3R0Ǖț,ǒz%\X`=GI~ U-l{SϼLHeuH+>ؐ.ԁ&.ajL$8E- nKJj B:p.DW'o$쐅?"4#4\򺒤|]TDW4*wK[IC bk7^$b.rͦ:Z~UA+ $aD̿R+G3p"fJ!a8I +b}%8?g ] W([i#4˒5dY`<"j% F;^D]{cIF^!%[?|4D+',6`>qzPlQgsO\ v%(VHEן:3qZm.TWfn5PrX0^'=o_)q8@i29pXhOcA$^ދ H꒤G!U}r)^(3oǮ3Ƶ jO{Axye+ymoNE*nʀ*!f- 6KdZm|Φ{'T:$ZMHabb=y%2=_̭j i@%u,1uO%=,eB HVw3 W!H]^i> 6E\ {1Ct#l=wY;X_׈5k]_}b&sQ#:VEzekƤPUQPtMN)H2amĝ[g2vnXF 99RF/$>$ďw@T47XvHW$1%W@:'^ Ut%,a\l1yǻ*AUa=J-:+^0*"4kUn5q~ȢI!P> e @UZr̡tp5a_,yϚU 8f+]tQ"aYv]n5tj:bj A79tma]H:^ B8&բbNb[stb.F|d'%W )5ѭ6zLd~aU* &{hDUz{Z ȄDii^1rkN>/= Gp]I#;[J{ \$.ʞ;F Wxv=e%~e^(sr[;N%M6bG!^wX.,Vޗ[4*C lg]J#./6,$) ` P{2,#Eq㖟] _>TqT(԰k:Z@eWsPA,6sv#60 C¶hYSȖ}>>_j)ch`W1i۟:q3ѓ{}z2#H)Jc{}]$U㉁C^*Upgu`o23>PǨL )Cl6j3M}吚1Ss>]\(Ԁðg1o?vms@Џ`i /Z_H*U+p. |gC3$hA.WYF%l)AסHi-~%Sk xoHG h-&P 鸕d'J:CJCf s[O5,P-U5 甮i W NNއ>Tj#v6 4c{“\ ]up!GC͖^čVUUxU]G[c6_Oʥx}>0DF|=gQIȅ%sg!Ovx&ŽB1_-9h).gMU,;JCMHYS|ZQF/~픚(r$E5p>PeTN, 1/6!#qm.sjPcfJ&PZzVpH`k!瑲.*CZ"n$}R iL7N rt<1n[aHWbG!rv1yŠ3}5%Sh!I֭Oޗk?.~ >mq(ﯫC6 mӶZ\B CS*D|PaDӪ`),)NQmZ]p53stLc8vbkPiw^xZF.(J+Fs#pvr>Qפ=!lbN*ޗqo-+鬨*d(x^*{lVذ|=d~9q%'>ef98Pz=)}v KǠ[^ZU1}98Eu\9z+Y(2lT)4Qv ȌUdTg9)UsG4$A|$D TjśbZ L[DF9o)ׂHm tn%F\Aty0W~q_$q):,,v3L.%5m/uO誮r/iz]A?QRI1]"Dic3RO9ϵ [z?Aq}G_RwFfd^ =74_(v4@uv (۔fߤ- J])7T}NAwp*kį@% f y=Es ےvZkuk Rq?z&G9 ngl :Ǔke4@_~1:q"z`4շmQ),wR)GH/Ŀ&]J8ENumu '|vO=s\e?=p!)OŚb\s` * ?o+0㓎l2O\+u>)|TS F$3 TS +Dy h*jJ+D h*jJR@a.j|J 7*Dn4dT"TJj T@SPe h*^\"TͲJ*T@S檢J4e*)sUZVMB h\ W@S@zD)s_o;u endstream endobj 36 0 obj 4085 endobj 38 0 obj <> stream xZK+ޟ_uj1EvY\wIUR-< p=ݪRI-q~$ bNQ_zN?sq_߼?ItR(o*]p*O{Oby~sW~O)g~!e<&$Jv:?\A47'(ReYCZYE|J<"w{gr_n'up5JHQB]Cf㧤Ow0f=a{;6_ JJ *y} ӮjPR9M7ַ%Zp q uu7?A)jo)lWnz}Q[d&'fÎI<#ȜṺgI8*8F~\з8Cv(oA YGxHKbsq+{Az  ɧ y* ߑ QX'tzdn2q2"G䦷_GTB,TyO4uP>T!`6~ŁZbCKhELNPQx | J U Buʓ{) EA͜"_jE}+ {Z{:z?],ϻSfY U?j`Nl)Ȃ GfQC9~"ʊ-2 Ȱ2F!/,ZeQw{^**> Sw`AJci#Zv:liPTU+4^[qoՌ-_}3 `o @aj:<Z#i7m>Sٍ=3^7 uXXʦJ=|e 'O2{\g=3ю&6 l?l T;"xG47{w ߄&f):\r4e$".QBULɈؔhݰ 1FdV+01o粃$T4sRh]ƥ-يI%@0(Bzq@ЦLԴ!&4Ufʲ#),ۯ!n I?*NZҫd+g{1GO2L== lwenSI˜:%({-畭wFsXDbYfE7|jjŞخdzps kA fb$]~EYZt(x Ss J+$)@T;&dK>0_"xHXxU~]9v^oz0Z3ht H$&2؄b&/;.d lViA.ۚk֒`_*f>+$Ma9l椟SvԽzKH[5')sg:EuRǒ7$4=vrۊ쁨3bGCQSuK]Ŷ%K~GWP+؄+xb{`mxн:MM:?>aG}eH]~4 6 Q=M)a*DQuYɪq7 B DRY8-:'a/Zg*Ukm*#z.V<tUEN %fI"#6VTe]. Í:~/17,?؆j{:eC xA'i}֑& c7.ecj [[}3BT}U%P#۟50TmMGزljS#UC9<EG[7:#m4RbG,W;MDhMjD1cTWr%00Oc`BhXS;UX]{*.d'$8MwB(Y;.e2b2(r}"y<8E ZH/DHQܐH6mCDz6_]h[;b QX+"-P  w-|  -V  Ԑ,J  -h[%j!YA[=A P-c  -h5DK> stream x[K, ޟ_tv4Ӌ. dnc[,U?Id??,\~˯q^bt?]~m._Mie}7ߖ概oe}Ko?r{yG4on<}O㭼к\4͚%}7/7mҫv(dž?ÁWx< ԥ]7FVKt-ݏϯtkU{N5/n5|ԭ z {m4#|Me+QGp].њұh?t G] u(+.k3-*eR*y[Ӗu:4Дb4U벑sNt(Sw Rj[;:&uV⒴u#hIylrG5[v.~o]ąn0qz/[cބ~Q݋- W7+W];J-/%|kWwjk|r< Qj+X;6+@B~9l[z)X" E2jWN^Gb4k#9E3-:Dby|IA]iǴ(/F,?hS%gU l0|Y-H!lB Q3d^ `L;QbyakLjUtE4OxJӛ2@sa0Ub؛Q-!l@4d׈Er`MoY /T>`lQB%[#sf XI}lSSǴtkO"&&3wV\4B+};C0"ґlhL$mR+|;Ȗ@4sM0b̘HsԊ\}X0UaO0X!4=xLS1☙80vR8x$ HFCafїmR9-\Uzu5Ql Llv~chs3i2%`H7"%Zڳ5_tIhq#kFwf"b?XqjkM.F!nCƠ\=+,FlBHʕ'ҳ2i58pũBuVlpi kcyo ǜԁ|)&Il%7kv6vƴMe%ƽlGu'7+:]܄0]k0X/ESE:a\,΋P_B_+V9`XgvA&p> ΄mN=/ΐ%˯{0` SPj_46_x?f)[F#5=]8:8n/xC#Htn?am @= TA~nk=TJ<!e@mp^Oaʀ6=Le̐vE25g!Ld}^ꝝWYڗ7bA)vM)GLg?[ ӱU7V_m7lĽc62y=c~R9pƁǭƽqࡎJLhHnqbd&Z\!̘f@;+iֿZS[tDWq+ 8bB(45A7iV.3 vcɲ]WSZ 1 W]ߠovWw*Wsi8(Ϩ+ ݥ&ʏ~JJs7fdY%Q]dʮ|+WN6<7MɾHޠξrS˿V]5iiiK{Gbea=xzoS mXD ȱvui2ITb 2Zjuc^ UoWkf~"b8H;}Ze ;L]ve@7ӽ^r\<8 Ua!·]gU:,Cr>YEs֯y-g9 '>&Ƌ8[C"Ɋl˯S_2ҟ/I^3A'oe̜ )ڳn>!Z +)]/LX쒓l+?ɸucT z(FPd0^^Vؚi M7tޣZB@gc.H>B%RtmS< 9FM8?<&&uLa7Wʉ(1 )oBՁ$ ӸóJ{ Z;P;=*"'GB(LoRP ߥe*Ԯ u*TϓU)BB풨:JÊ(T]ABS4 (!2E"h`Y 얉]BS4x&x`Bhv)x`FBhvp$x`w!4E )"f-Dx`5[!4J )X 9X%HMs!4KЅ,BS< MM"!4cBhF:x)"FEX_.i- endstream endobj 42 0 obj 3477 endobj 44 0 obj <> stream xZˎ+ Wx=%@^d7@Y f7YlJ⺻eQ"5]o=M_ŝ?~?}:[{ ~;Ӝ u2L毓op):-z;o߱˝Zӓ~RW{;mNf}a LYQӸmK4q"s+7E)Z 0^~CR0A ev4;~HSSl7/)X;d4@.Wp#X: LALt`,Lx .mx6J HAV\s-iq!6vA Nbx` K "][]7(7`Z\CbKm}F+o渚:ll9 E5 [ uΫ@,:h;4-jpM4꼤Cz aH A{HG9qς`36 ;E$"6eai=: \;cv/ȝg/Vy)Ɩ;0)rˑ5@_V&LwiD)dcU ?<뾡p˺eg;CD m{Gl`ǾGbe/B, ;wYvv <ΊO sVJ%M=q2b/8S60faGKDr˜8rl h}WFOK:= eI* |^/>hkVƽgV\AO2c(<+j?~Lœ֜uJ.qZQɪm{pat)zk`ϛ /W:]LH; 2u6%JGL"l Txx)2OmDV(GȊW>6ʥg+Ju9Z!WYR7zjҁMnf!M8l/vKStsԤ)h%xg/eccCRt\ة$]D7BW1* &Ϊ/S מ,PCJ| \y՞Sü8S汒K#q^y_P1i;PP_ :-t},KZUESq6*)+IHz 9AaԂKAX> dR/-jA ͢O֠yrǵFm3w<[9^l̊NBԾ'OrH 'Uy5FL/}#[ҼN^OQM|2\ݻs@ v9lS*%䷉T]gVSF7V::-DI$6~0KTz+6.0Ke!LsX â]ç 2 j1 G6mv1Gã3BCuFy+׵TX@HETrftTpm2kss,C1IpİqY|[ܴ U*EO^R`6|T(T( egT,$9 HXh6]z߃l4g=,zH4߮55`^M $-m% kjwkj|juFrHk(jbRQ@HBEC^fV%GwfJ_4SfsORYY]tkfa;\S.- J*˾lQr+J_l}#dyYgZ$P(eMX26*}}`yT2w"|~Dc}үeQq ҺCM).aؚZ(nCM ~'4IҟB{x ޕ˟/r9ݷou6=Իݷǯm~$t٘aN;´'*zmOf"Ƴa=2=矕 ' %PysX C #!ZBG%+%"JUAa+!"T"BR*!"TV"B9+!"T"Be*!"T."".?F2TBCD^%4D +!"TV a;x.1 @W C<AyJhf~>ƥQ endstream endobj 45 0 obj 2878 endobj 47 0 obj <> stream xZI+7ϯsv3 r9.Q-J%nJZZ~kPOљÏ s5|exj>~馴2Me)77T_M#/yLtV[?pSo>3.h= (= ->E1-M_=T(31篙,#t5" d kXp㇗/ MfCzpTێ|fa>o)P"Aʏu5& FA bɞdb eQff6Eר~c9)& B[D4le:"vX,l'$~ggUG3zV (JBQUP1#,pw}US%\ bLY®9u*b׼PFHzS7kR²zM!,]y +?sVbc*0k{E \ʴz̄9Wu8 Ÿ9_zgtE ,&)FTETAK|XJS]ul3ٰJ6?`̈́]f Ҳh8,rcgwH5+`㯖?i!pW%rϒ0+:s&R9znajUn7 l ZE4\'u.*vm:[۴~ikZFs> stream xZK< ϧs;~Mt!Bnr^ǖdYr=z&JOr=/T;:ݿrSӟ?ɺ?v58oj\&$og{5cV O\[XlOk`/oFo \m ڶ7imRYF%;~U|oxq_yzeҤ_m]vPJlrI[=Pug{| L]vvoLrՓcw?-iM R p aoKInsWm(OW@7h$~*,;uR X;az45*%huRE~0xFk#ݽoqάRr8* Cqf !}m|hbt֖*ƍ.rh7gIX&;+wGOE(<=HnC —K^Ec]/JK~!eswo]MTa[Xz(P-^A?C*``dV` xzgnCWOLYH`w*hgahyy>$=]ڃf08tqdjC]K*Wtb ˀP d2B=X;`sd.n<Η%bWQ4+} 3#$&^r|5Ɛ)f~>N-ٮax=:):{6\Ɠwxx${D<߲gg#u&PNF98V>UDadSʐy n2!4Sd -U݁Eb)gX3aSVl~Vn/"ڕIDW=pG4؛+CT%blL'VTu F% s(tGl۞bɫX#DȌo6te!tK9Ba",54b2}UW&vr{)]d J b"eBȨ3qjc fQ_ua9fo\g(]F~Բg_\N !ks:wW{ϒk|ugiea jR9UZ*Od,61]JDJxhn96{\,bb2Vm$UrQcl2bi{EA9a8Öu_C,yp)O"+郱޼k8>gr']x]T 1DAhzi6AU{Lf+] KtoՆ<{.CӖ[fĔ~8 3|9jMc.9&@A/f+@:0 Qn˘~G"s 5G;W F4i?3oDt!tdHάtG.s ۍlI 1\(nrRdawߙQ6! YFDkP+yTlU/I|M=1ߌ7W|0ًj5`5VXIB1I$/z@`$?NpWņK5\,2#e=fK fT m&V62eޘWP|q\+g6a ~O#4Aw{T~ع "\^dM?°!Fxw1n{D&zzg$obP5+NgNi =|(ڧڅƋiU^_ 1cm^!XSjq#Hjucf^Ҧ]qs*fkl8d֥=s"? J-D!%cO?KnH`4NyWp˺|ք8\1tS @RHdH&@"~t!"D"^CqDEpd\/(rD$rH4fh91TD ĴICX82ѡ?v_hѡ?خ=B@wEtS)CŒy?Q{ѡGlVhD#~9Bw endstream endobj 51 0 obj 2687 endobj 53 0 obj <> stream x[Ɏ߯:@;,[^d,2YáX)Q=\-brUeIZ}o?/\~ǯ|/uQ?m-~ u[#?G~Yn˫-G|m,WZP\L̔Mr,WeaX/% P!UXT^ZeG{./_߿ID;T iK~3Xiմ+" c* 6)?z_aqpF7H&J"PeUg$Q'*Vv Yè)^ȃM L0REB,rʀSfNp4 G&[ #䂔+R_:]D] tf;"o}afeic@7`ANZw9I7+yY@m N5f7m@sjb0zp\?Ʒ%)\@C]``l±dnQ<6^6G,agT5{:9S@[&5˒9.VPLC;Qـ3벘GeÓzV\a5ȃkO +I{0CaJ7@ |,6۾C+*_Xu wKs_Cq^h;QɏdmZc pP'tkVa2O"԰6'hɰ"72\<͠IꍔOVH1"j-MQO}Æ` r 4*Ж~V`RUzًX|-tjF&1n8@ F,Buh]0Pfmd-L׫5_#ѵ'?[XS|%2\%/VI0C'4N`+͂تL$^i`VkdE9X٭ ^9X8mR`O=G#\*!ӈJЀ˪j5ˡ 8pzY2Xj"ClV `s̔R7$5LU0&"+g%f(+ v?†FJ* ʵ"!벲.O~}[<]Ӏ'SS6qcvD; G#BI`k>S?IYxiI3* N3O 9ΰcj`K,Jm4xyd{&PH^0e+7`Lr{KN9ꆙKXu]),6W/Q;7:E(3cOŘtHcm^VE_tZf)%cy5͠+:*3FTvl'~z3O=i ͘:Rn62^q~Tirv"U52`M'ܺ4ɡcE:0y|pGCރjujRmi'O02m8IcG9OD?x=s`@0ds8s^yƉz2wvzPc֟VdywW3[CZP6DwU2^$aNkTմPeЄ.ņ5O4Yi1;- s[3zDwl^|zd1߄ezEa`HW3Q~i5Ȝ4U\ s(āU>QsU$蝃O=;ǠY1 fR?Gj fdZqiܳ}VWrCд'u7 BF@vT'ˢ5k|3%Rtz?b <)SZI^ i]Mn G7N<]Si Љ)^c+k}5ĩn܏$F2+P$6wgaAou urEN@mgђEȫZnQ((GXa$qp%dYp δRٖLWhК t E-|Z XOb-B[.x%"(wa&Ȫ며GBԄEh%ABIlIKjZE %b3,-ξwk-ʐvܔ)DZN-8ؼEsN1@Gȟoc4r!d asH{79 1,/5t3#K1V/&ehxq'zÝQ0ea]zb p:V| [ A5s<&}ByKCϖgbs`uu':UDսR8:&-ʼ1k^jB/,xBNYl8*ʛ579W Na:ȳ,{kx#cy5pauΑ =(կ7jԳ`y<5-Y̱- VǨA7{)oBkL./-~*mYUZX&@^ȅ7> qHވ m U!+ZWaƞKC1bI qTm}_?e4 `ӞG~A-Yl- koOnX"Gsy #˭f+G,Jn &;ӓle-dP۰󙎡vJ2̠Ǻ6z&w=-=q1#Hn͑Eӿ'zcWtq≙u4z5zɍ3 bæC(Huo oxʱ{XzXz*#3 :@^D~JD/ 0%7:%j/8%w Z'*24\"5#bHOTMрd%h`JhL4E^O&aMKFMN4_MΫRMNHMhV6DSDz#2sD: "hI )"X")`ٟ "h )"0$S<(w0<_ڻT& endstream endobj 54 0 obj 3912 endobj 56 0 obj <> stream xZ˪3 ޟ:`n] `EvL @f׏.U%o>0vwK%tjw}oK:)Sz|rRɇ^wpuvnˮF1pUSŵOu/W~_GUٗʮH,ހ |’qʗaJun|zybfix;af!,ďY Xr{||O%:ЄM߿_C}{`[$HsB#t@(r H}*d/JU! ǚ2,]=Ú-zrRۤ}L1 堏 X whxz).2}gΤW} &`@鍄-_(Oۗ6$ؠla[bjS\M20==$vo*@֊-D ¦B](0Z]gR{Jba[$-5"GPC2iS/Zacɭ9EHPȱGɔʟXJ*[ k/䮒G:9mFRwaWCeMuq:3f8r1k&}6$?m°4bKvșMɽz#,pn֒q k }wc= jZJNs.$֔jm*=I>8myo.|,$;d,-v:þcJXxee1Tx8+8@TN K[d-^,`=7#|VuC{sfGk^&+ +\;T}<ėU1eaI'd9ket:Y[,n)T;,3-"䜪Hjj_c76xhY'%AˊcE n[eí[_ SkcI.@&ʭ|%%HN=5+k\\a2*ot~2:#N~SO;:(.b9 ԭ".^j%ers)jU]`7wXFw"E_i[#W jjR! Z]6m`J=/'uxӷn $7I''zmtt)N"Uųðf߅oouˀZ+;ЖL a/|H [TUz>F:qc>h(ޗ';V-`s;tjmS6:Zo+0~vQ T&hieBw,xkU8f\"KS0؛Bp)usі%Gw#x=t4;!6*> چa GmNUW "v̢{L;Pøjm"-k6p:L􀋈o2H{+(f.3']XQ3"?4n U?.e1GmBV~8-  \{7ݰtm:P%fqP6fEc<}mS·1}C;\O9E6!&?0{vwMPˍv%ZLwFZcV~&1T8 PJ ~īa|դTkE}xM Ҽh:ޕf\]T"tȜvv P<)y9ZPa''EŀuGy&}[@,pS-j"Hrl:4 ZMaTCT\TɭQYkTIMԈ]@Gi5݀&P87 ,sTz!<҆>]3g# zcP\T1բ5"Rh!c?Jsp RXȿ2tPͣ fy#vv, 0 > stream xYM6 ϯ@R8N-0@Eo@ҿ_-)4d2EOԣFaAя l^9(nx~0`F8> ɞz o ?a޾@ғ}Uf:N ^DO/,WoyUO'\&n2zOP2|bYiŮW2*V$,I8D(q/c7ddr >y(&䅟3˱ӯo?^폎ryS*0׮Q{XԟK2T}2 -k+GEJx뤅wZZX%7-N] 3} JDmV-5nR5~TǾ`TQzD;UWFkk8ЗKb+CnBf+<ىܒӀR86t֮t<39!Q{%Ԋ +vN/{Y2jPn\!ΤZ@G~$2d4T/w|Iw1͵DƤ{`tU5V,1O1Pvn us /둌Ι&So>?5[7ʏuQ p Z˔[evmvkI*+R%҅m{!'APό.KmHf^ [ 2Rq.%wA4~! ܝ) uxp7\r,sHzBf(I-c)M'2 endstream endobj 63 0 obj 2374 endobj 65 0 obj <> stream xYˎ6 Wx]`RzY@ Ew>. n%);bܙ 2×ecOg\;C|t_|^b<.F׽x{㗣ƞѸk4GL<#^陗 rbF+K>S8I^ܾ,_Q$FzNf,_͊SE(1ofH`XUg~֯yvǟ~}ez{rIwB +^LeʊKf^C]r IDo!h6BKw9Qb.O<Wk?ѱ;lq\m,1|q1ƭ86J3,qaBZn*3==0|cX8:*u1;*b(k+dmcVۉ$\mP{F! 7;96 a]F<{n q:2'E[W)Ѫ4 Rag`H!98MJ4:zvc"h&8À{A]::w$;rHyf -=q=dNA9?9!R(Ϻ8$g3wv\Yj$~#y$Гhl#m*LQ Ϯ̗RΈ31|NPE5צwQf&> e׾dCg{zso'孳Wd][J]z w-LzW")8T C$24|\gA3پɜߓpUW$r-j `nhHQx:-/ ^xOqC$OAFxN"6pVf-Qta蠥'|bNNZ2/bf~ wN RU8kk\74a/ݣ z8Oӊ>Nc#]Bů1OvѾRIX! M=Kߛ>}>>8}lr8{ZUL&ώ=&R5;Ӝ{džii{Ũ ;s9R[*4dscNK>1z5Tg\y_XlؔZ+Ntz$rln =QS<^-?9;vITGT)F]W^Zqե2Ƌ#[+ЫͅċZK'3X|bSچSCl |mg7VuG_TS ~pÌ^7ܴڊOKY?xDV@{8"1ڑ/ .2{2 ㍑ E͇~'9+)F4lpgOiGn!tpO195Ą2yfo``9GO +ޘ<5] ױ*^oJ. >Uj5qf5;DUjD@ZÅ FgP5+(zW@*#qY[2[- 3TʈzxT@*#!P:2ʈzR@*#1H2PʈzΈz|P@*#ꖾTFy[RQ72n/ HeDLʈ+ uV@*#iEu;R@*#ƢTFMB[RQ2. HeD]ʈh- uaY@*#"q:#꒰TFFbė?y endstream endobj 66 0 obj 1748 endobj 68 0 obj <> stream xYK6ϯyJch- @%?UwVOrK24vKR=zXt gMZvo_>uqѽX;Zݽpd,@H3 ,"g1 0 -Yy*6j-,n{w1MkE+bE/,XdD;`0Dek!OXv0; -DM.аv)M Dܯj&Ea"o!7L[k]ZoIbǢLa~po#e].vxZ!.6%R&ܣwe5C&e}6d#qT78aBRUE up f=&W%K~[ewj,tͭpPq1.\?7qMVM^~`!#񿺨ѓ6ꮍ)wiɖJ68 Z2%Bܤ ¯V+`huG܉GS*OJ85k0yo6[4E>p^x{kGOj6YNK0jlb2FX䏏F~cGF#G#B>1uz 5TGah2wc3}ʊUe߿{XuDhFՆON{B qF㮘W v T`ƤۧG4+kVˆI'#$lEtVc=Q>R'GZmchjU'35H p$x#LOT3d&9rk8Hx( *&'⩧"j!+&>2C e&"'5Q76DMD-B&j"Bs(#sF endstream endobj 69 0 obj 1602 endobj 71 0 obj <> stream xVMo0 W< )J>V nv^GQNvCWH/"ZWO;3E_PVo:@@p;hÇ<;%a,~o:et w>0˫5> 6}!ɨYv|EqQJ@2%#%qU+B9bΆn>E|s1>6lV7͖c!mHCtDk,*Gw"ZbQL9 B= 0K3.(o )F(ٌ!8#'htLd)9^؁v`ggЙf,S+2U)\- M'^4B YK3Y2kfBR5؃.Ҽe¦|p-s9r897cs:Șghzb[zKBJB-˖dyqLYB>oaIT2Xg FC'T*>ܞ[X(JV VqPSoЖ2JMFW^ T -Hy 7Pw@i:imK-2lL+.[J0HKpJ[nA$oWAYCw!NZ*=)J}rіsEq$kjT##(kd@y@M5 TUe O0TՐW3HՐ! TUeMj@U55+jkTUՐץᲾ$HU y H 7@I wZ endstream endobj 72 0 obj 895 endobj 74 0 obj <> stream xXˮ6 +.p]zY@`vEw]bYAһ(uCGP|}lկ?U,UKx_{S~vUP^i?-^SjKh?׎onWnoQN|%@H+m媰Y| fY8̗%2v9%J`E~qF(ߚoñ_.!:3^rpa,!1j6y'j[lsH 0J8j.#ZW7H1V`D 3hcYQj PsO(6r*yHԋT-(;q,PN,5hyD\ efp:bIK |Z"l u5bGjPn NA8+Y:я,fhUe& u[̒w-`a4[C.z);=K2ӄ@,E>' &NVhpԀ P3>`A=I(+Ru:3;x3m5::m(FG19FT0ABXVu ,ߔQ)4`z7:Gvq I 5RQ=8_i䑺 $UOrb=Z>xփC@gXx&ޣU4FDZ(eut).g)1YDн-g2oJyԾ> stream xXM6Wx]f ,{tBM~Gc'lIi|ystf|Ys^q o_hZ~xU@!;}pW; #lW?}xdૣ/?*:N_3)3> 5]AUfb)u`\"[ɂ )QB\%`W:G_rL}6tGw˗]&7-fR C!51#'ES^a0}krKi)fTHX%^mZX c,A2P2N'1 %C !OȯU'S)9uj8|A>sKF׉ uϬ\t)\9@9؋]nt.^8KYiF@~,TAo/V vPV-+4x3hi2d 2q F[J؀Jn\ʲVJ`LR ?rTF E딗V`oudh Qh&)["j}1Ee kMR^*2 CmkV-s#Ia Bp4o椔H,o9{ByyXl(f+){&L\fڻl`Yzkz/dfνNTnPLstYE+hO'SU3'L yW2'%ja@V98'$?JV5AsĹҁxwhw،{@:ZB#?UK ߧ(osՒ PKމ7wΝɬ;V Iav߲|ߝ>J|#S "VMš(mlB4L2$y|EYq@*g [뤻 ۧ|exO ߧxɓ.߭EMcNYC)[@bdz rDS" `\UyPNB\M @BdPƑA4JX@f}ɐd/A-JSM*(DTA$EAhIRUPLGP i{AU7lUݰm:#ꆴɌu7}TUݐ8Tuö.!nHvUݐ!uTuöDH iҪ4#; endstream endobj 78 0 obj 1177 endobj 80 0 obj <> stream x\K, ϯu'GU4LɁ,.!'ߏVdeY]gzt=4u~%o?w?<N9ӏ~NΟ~&?|\/S.z=O__ s{O௛ 2ÖwVt]..SlE駋JgiA en.kւM`O[ o׿돇_v kz UgB2\:}Ngq^Sa^QS<SŶח΄ug3]̯Ĺ}~irdF}r!Ř/Fià :ϓ `'?S)̾tWrVY0QZZ RyB PYZ ƩLE\\C \Z*O(+Ni?%`gaq<W8>s(Av q H) H O H ݈ v }-*RS(Ʋz$W9ua8FxRh9L$7Mg*RY}%3ĕB& qܷZ$rj{CHph2$|pȏEm)cD֭bn!䱾\:.d|KƸl.wc]hTxEutVpBL<;9eNǃ;}H!%LwwGUt\<᮷/q}@‡Ý wXqTx`@+tGR~saT{}/>9-C]gTF>Gp49JhtlOtFzA!4A-ڟe>c{ JUӻI}cƱdҫ̨{AEh^&_p/s,؃qMj:ɊU_@02Wtfw\ -%4'tJ|ah̅zjj炫rf.M^6^DgW6Kr~P>ޘ6o:a.`'|X9t ӫ'<퀻԰i* }rChp&WqArIÛ( Xrp2>/ݩbm(+hڕ}u'1957PiKä{8`ۅlo}b!Bu|BzX$5w1uoo/7;q-LM 3p69X8d$;*=G6{ qF3{u;} ?QG\#x"~`*(d9vǙ8[0R67{9>d%>尿}Q'Lt_sw\Ṡ׏䆮!w2FXMHT0C9+^ $A.KQZOUɉտ)gT?94;r$sd=UKͧt7ť-訽¼ӑ xK^3A=ܪ|jT9w㒱w$c[uu򸾲ZS\^dO3(0r=" T":&ܷ_ʕ l+Gd0x";ݔhw/& 1 O|#L9ef?LFY4s ~賑sv.@溰$[+mI>](Y ՘Ɋ^~{ǿ:㝸'^[لT/Xtŏ. OHUĜKr(-^cC628Wv3jI&ta!vH+paMKun_ulv+O:;dnIRn oWiiNxlRAȁgj6jEEqȮ>TZ@ʗ~,^%rU͡ER f060uLӠS(昪 '7ݥi\//A!#j0P ֍P mQH6By(P&% i~#ԋIHFh:RFhh& -E0v#4na6" -"\JMEEr?J@ endstream endobj 81 0 obj 3537 endobj 85 0 obj <> stream xW{l[W}v'Hwmf IGPs?~]f@(lKIt"SQf>F^L&8"B5V9#]t2e=Ǔ Pf}x.Dr`A2ԚYD LBZ2%mA߉rcp&El(=MStm :xfLMtȆB<8AK+،Li@9df&hja9d$jbAsf6皡,w0]xe~ ȻA eO?'E4§m~-t0L=i:77 .>ѻSP U]]7J\X5j c P24UL^ jZ up$Fǻc t >z }(~T75_(/Aqw6Dϻ,  .'rԱY䰈c4@;LoG6UU_dE tJwWa;㚈p;kX&⮣SkUmb> endobj 88 0 obj <> stream x]n <CeJɇ(NA}m@f>EsK>O'8-B'UVMʻLTZnN- 7'^B/JC4ԉqBJPf⻙Pg:}ZnkD.Ulp8Gc h\y;nX$\~QX?m.$Ϟ+l{!nT^0m endstream endobj 89 0 obj <> endobj 90 0 obj <> stream xռ{|ǵ8>gvWJkI~[e[e[Olbl`` F6`6!!$BM< I ihnB~%moJ6}ݝ9s̙39>G(K Z>:! -0ܾg  |!|.!Za:S 1 w wR֏Dn{?g^Zw ΤK{.'dX5=fkyBZKݹgM_mW#1BdC}G? y_äS4Z7Mf`SR3NW;+;Ǔ-(,@IiYyE太Sj?&a+?Xu]GKNC2dWH!?'arjh@=9%yaE|&ϐGc0Jq˫!B8'2l9fq\aE&gPG \2;}/6r+s_$z77y<2)=g㑇brG_:/hok;yM376NUjLTYQ^VZzrܙ.Gb6 zQeXNtI fsW>kszb9lW%ޝ;ngpgΞcc[fqq;äkpasw?Lu68)0gaGf=5nWE˲`dyr:%2#$KR]ݬxl/lj泧ǻz[|ZɆmr,J։TM] 9uu)Nb[q>ȱHJ7axݱ IYXnF#aB鉍QaktS/sډddկhPΤ{iVW_Zv쮋pt'KOH YPǀp^]vtJ[Hu8 } DC\ja:4"FH뿁ݑES.9;h*͘ b][6[沩Z[T2d}u18~ RMi4LkLu-pE?1qFIyѨYIl;;}~gXi`}cQcX]vMą/7joO/vԹZw2BO6 Kzv{pVÊr?[;{w[;Uh 7ڒH40=VΎS:oo8BN]p8 :; Q\rY&{qi.T ![R^Pߗ,=FybvZbl;@'vXf\3o?ٿSČOہ]vo%vhl,>bh];=b$enf5`}١5fs/ ~d?bwa6;ePb;XNVBϨ\^S@6zfxFw۹a3 XMF V҅i@Cx481Dj@O }Z: ],L[] x욒]^5{LY*{{_2v4ukE3̟ABȠDrE= fNZh ([j Ԑ1  .t~f %$nN<'*vWcb"&?Fځu7ɾ+^t6VsPrrۦZM; n MΠVe0Hr6f T}2+HD@9NA86vQEQ 21p&Capp0fМ14 r ^2qrz-<8.6ǥd_ $kPEuY*-b?NHW1~EBT,SO:;dq|Lř2-3OtWIj &C߬>A CYY9$z7(8!|8 !_|ތhEԓL %Sh,Q(w OB6H+q޾ʝMׯY\iv|,)}f/"wT{eM^dyɝ}ٕ=f&cFY+ .,\Z49dZltWϿ>﵌?$7Rݝ=MS&Y5GxƻPu"wcvλ޼er?wJgٔLqνMv7;:N>B*:I%Y%-%RSC&0!H5 lNh sᛓ55(1uӊqɸKQ퉶(CD>{行O ==۸nʊzͳ`v;hM)TDҦB(zc 4s!n8% \Ьkt:LTs-8̜RFiy-V;Ρro۱#5))%#{D~9fsN3Euj}Mf2?q_'i=[.śf:6vi|@D7ixMsIy̓O?<:?σG`_lσ`yѼW(g22c:cVꌹ7^oa053~fꌍXz/}fԈ;/£^Ko*/vzi>^Rw^؅U^:NT/^{z^K7zӻKq5x4gx´{y/<2p1xD^zRЈ~z*c *D^ *}^M^X]=z/y/^ g[S^= BJL=^Ny1q/Ebv{a88vtr ;Byi2&2Z0؅^^*cP ] &/] Kӽ&Fv^k}6{C^Z[yF3*6{xiJalZ~ w{RjIJ ^'tbou#j8)f-l.K&1Lb:շuq (Ouo*SjSo+o7-b[zOpEZ%dXV>}\ԙfQIEޏ|Fj5Fq(|Do{w;fefv'nKPm)YY)>SgJ\qGf:hFqe2K dflf;G2'{C"y$T%(jv}}Bv˜4Y1*l@oA m uLF$6`3S8,ꎆsgxFH9Sh)옽i^Ѵ:l˭q& u?{h$m;͙[O8s&g5TuLδz[o}˜l˫e]UhlX?'/lȩγi]"hVj*jZ=g;237t81} ul=ib\8ࠧs\\z4VCrSG@'& n2kŶɀ+ `mepʖj`k[|q=?xHs젙QxyP@ށ]FvRRV^V3Yٱyi3&/p'ZP0R>g 㣟*e2ҴMi[Vfz MشĔ}ol^}G+%Y2}l1Z,Rf6#Y{fAξ-\^ˤl$825-v-jK+SKLuqZ^k@k % T^/puiIsPFsy"/ݤ8q stϺ/f"rt<.1MHK- 2EU!ϙzʹP'5]|%syco;ʌ=EHqIpNe7"gb ,(Lb}`OE>HiNdI$jOd&Ԏ\8֙jQS/{p?4oz_O,T(8~_֩yS旵UfM[ Uɑ[oTV-j?n߳f6wqdr従+ɔlNL{Ь7f% 9jP|g-5L%M+~a1uhY)OZ[z9gLcҟoڱiK9R$ < {M.~^2B~Uz}sݵ2LNϡ7By'~p+J9KKϝSu8EJ,:/ 5ĭ}Ӫ.q5-ThkVx2C#8wU޿(u޶\ͻj6߆VչE8'dvcG2-0+l P"fppA-qR8-2{_m/a#K~u?EUJ9K.JIi~/d/dvxS g~HȐ1b2 RFd;3+T88G֍ Nq4'A{dK"\?s|]35?}VPتx<=8WU{Ic)oGy^#?`s%ldJ-NfD1e2''Se2&!moN0|_>2NL潴Ky3=<Eq/M1QmS܄L2inLwLf{\Ru':~Y޼哛\Wճo˨_;'"CoH-/v$L6N2 tmڠΑ, e*,D8/W5qڲk)o8FuZ(ht0BpN57d>oYœp>O.Pl[+3=L{{?R[.$I(5}NǕNpO*+6w #XO騰Y#f[&i5!v3qL*W#Ԍ? )Eڨ24V݌="C͋ʆ0qO;Mm9& 5©Cxa-*nT̀p}FlVJV~ZM"h5pN=xz3FwL೴d"(ZK/N')OFLWX P&0麁dWq aXFðUV0I=@b~Sf%P:E(U׺l n⅝!^S~(|7spW<{!eqgO5~xFiۄ2rdҤ&t^RkڒڝʱS2LL=k;MPƯ "  á:sE CH;ih8ŏ~j^nGlm۶kНm٪ Ke, aJɴiԏza9eڜh2ß*O5gJg5M1:0aYc5#p}<$ȭU.fKa=K|1C[`&)_wˉu5Sks#V)3Ҧ6))-e9wvM#$уT[:MN s}MAF!tKj_V:(,knaW$ȒJ$ėAi tQB,)!$h'HtG:!]$LC1`65ܯq#w}|z]mWy+dTɾo:/K•'臑p&h,Z`ӿ iS&fnU"땦@'8dÀ^1e))94'=\5 NCa64waA@m(y,wa\<87zkZrҨJ N6ԅj;qWŜ9,ʟ,隿J4}룋o<2T5}h-ssa{}+w5%6.g؜4iһ}n5eUdPySz$Ӄrljk쳸.s>ɍ..͑`lb $ֲnOKKޓu&^Ȃ,s$] 111p `OQFEN=FUĀQ=I\P'M-ե,[q4|Q5?XF7A͂h'≭ʾrSr\ fhOkL*^Q|HuĢյtI[]&khܩ޲?Jl+{c7i5,v]r6oʤUUR暽S=eߋs0QʵuEGu%\pA=x:cf?,1$633ca01Zx8|v[,r%1\x) *ʢ>~z"٠LFMC՝pH8wh֘ do&jP;M'%~D'`h (N@|LZr;,0i=rt3mI/D*`OM7D8\x>d)Tj2,x:Oj`IYJM:\1*XR/${Sv $5b<U-03EvIPŎ:ac}"Ӯi[_/?PC_ Gzfu㑯^Wvaʦ>9vh{Eca摛DC sq bՠr<\"qQc1+4cc(AǢ -~4DѼ~+!ddLQG}W\B0!*u,9l蛓y.o=Z;[Oݿ(U/Xߴ`Xq˛x+ms ܪ?Hk[T1KC멏"OU8X"NЩ 0(8BBdGyW_{WL@ޯ8Nl8 Ѳl9֪zUqN^#.t.k$ќgR.ЖRd6Xb1hRu>ck*QJuLF)b1O@ݽlߛW}ݑ Lvzƒ}]WM|R:;=cRVFJxc a#䞽#{ԏ>ij<`BŝT:wRu,y(LVmYJ@G:z$.:zפ%&V'3̝Q<ԚMII<5~~Yv-->efYVP)/Wd,+2LW;dJQΓ)Qd(2V XK2VLUq L_>LM2Bjff`aK~B~M\B2^IL2gd_=2ȬZ3)(_BƫsX—b{%ɽ2|WVr XMhN/exUyBVDnGd^jncV|F~WRW)l/CcegUR2=2gV 2!%i>ubPÜZM*w_ɜp ۏ~b]he8?رh'ǃ6zSǸ+cGG'DhJDᗑEoؔbӛc|3qD'oH3>^Η_^UU(..%l|QdF0CRHjj2 Ձ*9sTˁ8Իڕ2Ac@dpAJHIOR)4u_M lNgodC)Kst\f긧@i~\)*gnF`1m\nkvVORMf͞Ӛ L+6Gfה'ҲuCVjl6;1ټ!;{'eyCo2zx{yaTy ăRsQI͌[<-V&0m>p(&)+"F5WX-Musژ-BR.!Yuר>=!q|z/xZz6,ʳVZBiyn5r\*Sڹ@ 'HR?i3 q0_F\fgM,h]6M^kH@v1|}搙fl'1ufjLC@ǨDQQD[6d| \`>!D~0vN݄+j#J ӈР7S(u=Ey9!CvE~ a(>g*3'q88Y)Q1}h0SG[U;mmoɪT蓐Q€?NA$nhoʖS$'ȇͷ?;Zǽh_Ǒw)N)vp|רT:p_7{xWɍgWٕD"?4 $>2m#?wϯ|#Zո w/_$5J :DOEEBZ\zSW6UWA5|#3ύ^cxT׵T,ve^'>&y2&pO|E7Q DN#kiP &rKRV1"<mQ'AqHKD:ƗDWTvn":+¯ķY,%H1L\PLQ]E>x} b,!acj"<">}DdŰ嶴7 D>Sd,P,h>E;*H#  hh4f1"3-Q¼Fd/ZhF>!d5(U;@$o%b`;@K+7pOp_ocs8ṂryX_ 923Ҙu fx<ם7%`ƙw1f,<؏>9d!!a|%2JbͩļW/J%"ߏ,z'őwJ[~IiV?积$דW|+^ၧԏG4zWT%jd2)I&۔R2`jpjszպwIk@D7}iY)YcbF=ATcooz3$yK?+/I3~ns7SI7U[˫׺'o84 wmzʪ`dmJ(W[=f ' L2poD>myE%Kw/oKۯ @C~vceV֤cޖSvdQA>f Dbd'ʖ"R"2{o JN^cjɈe&Na6n5!aMW(8ulK23}9E&j@&zŬwP'Z?ITHhi$ˢSH6bq-6mm'm,FӃvQvpgj8Ɲ(,@˫n wܑ"{.'f?"pn\rPy!5̡,~rAANG~_Y"s78.}j @=YY)\ š;̲%'[WjXT4;XLG ) ~&5I3f7UWBM+k"5imosME 3Ww,sT<0RWbߥ 1cY\6&TQ~F!Sߍ99E/&/d`$9g$`M.kV">sп_~a=;I5+hYC2#B *0 $ 3.6/hΎĠO6<0rwɬK.KfzuOD%x ʡs*áBMWn@S%G9qIF+gN%M#W{U&MBLUh)S31}<$$9 :cs>㮌е˕0Aݷm;}0 5@ H9Q+<9Ew"rh<tŬј,hsq1O4[X]X*77d(% ऄj%yRh0љ_\Z瘸dKWI[(2mha^ӀNgP pp@7hZxh[cVNV!>uꊆJ9VG"\tƫ~;s J#0_\OԽug/ Gx9i_lqZ.x>vJ͏3!jZ$>J_%;8v]C TS@W CY=/Ȏf(l"+/ NF:GW d|O !SWv6udvNX&N&&|_H* |D+qGI }JT}j.lg ahYDZ+ZҬ qa^#^[}z `/^51{0` | $.T0߭lbe۾߉R]o'RfZ]`#iL/ؒqqЙ(sq _H'4*1/ M21@LD)X#KJbiđbi wZrI,#Vp"1B 6 S'ftd3슥KY\1z#d -dgciJtX#S臱4OrQM5$[KkɗXZGrq,iR"B,O"BS,m$4V,_1↾^goH1;/[7604803b`msΡGC}}CzfX-wX;QR'+y}C,/_)gŅߨb[399io)pN_ [bxo3Wu9[zF֎8{:&*6/[biohFV XZ..FmsX?228zbKh߿*87bZ{QȚ}?#ǫzlX6r]P%+8GϹ,[z^K;{.A0_zJ)nx+rF^ī{0^2SS0Y0k>{cKTub>ߏe[>Y j[Ed&/Qswb0 uw0H]k?BmrD-aYydި,tT#rˈ; Bݎ*TZgDmm -6c˰>ȥ*n6Qqz%Y0Bz}ƖwgDJYj:XY6H&&#ש""5!O덐ާsml܋Tkp4Y:>>q1^>#HI:V?<\S02ձXkibٸx|JR;/X,Uq [9;ZSu> Z4Q81 Q%ޯ~_K.]]p™ '眎?_g';LC's8G`xwō %79-ܘ|W;};9u-=˜8 z >yK4bq(M *gz QryT9}t{>zp J|#=,=gHLct$t4'{Hyt9to:$O >)SO<^x%qR!<(@ljC{^[{ݎ83DZw8LihͻvĠP0[MЭPvsfX^x B p ~A?  "S6:eG $'ڵ]ӃuC#ŝEAca`#Aykp4 JknAPi[BRp\ǜ4G ^ytAfz $%/Lv4]?˶v Ͳi28#">d|Y94h0wjL!fo2LͦӘIͽ`Xt l`VvlnSX۲0 []& ;|wN_ń[0aN?l' GG "Y.L5{xxddD {w;^$ñ az0/K#j&C~$\ = endstream endobj 91 0 obj 16587 endobj 92 0 obj <> endobj 93 0 obj <> stream x]͎0=Or50Hd"e5 H ,R^Ŏv440op>&pIv컹c-}c$ܻ#}ڶ9|JSO?v1-s'Mچ̺rK˿RkGJ3>MuoIBw\|i~R] endstream endobj 94 0 obj <> endobj 95 0 obj <> stream x{{|י-Ȗlkd)ٖ-ad ?lllmHi1 EBIIiI$$fnW-nޅ&$;#LBʚGwĈL(}[{G~? EŶW"|ٸ( Mh[v B6BJ~;GH'Hj+~1s7uַo܇,?eXn{HǓt{p.\;!/u{gQn}ҫa0وW/gy/|VBF8ׇ/y.sҍ5Ma`;9ȑɞ3<Sd<T._'wP'׉V<=%!Z$38xO DN 5 +"pt'>%Cǘi`E2je:ytzأr9 IYP,dqcL139g37X-_+F?ռdYyWN2$A3_'LVj̞=^4bh՞'O( Af% Tl _pJ!ض٢w\a]m')pϢy{ZKY-dV,T|+ !j+V3@:L34Q'QuZ>(A1ɞNā/Sԝ1 آѳՇX}(QaZ p@imRRu;'~b-);ĿखO8~$G$'8V-Z'nkꮧT<25(zmn#0I:rNm?dRRtUt(Z&,XY~˸6c(>z,*FK4553riirݼ`A‹Wb$mq/'t5*- mϊe✬_ۃ~yH >i6.IуL15uOP[U]`WDs:XB&km8|{[XdAXLsBnO FAyt؆ƴPgUm6-XwgKW˙s}PAWnдpC87wߎie~)fKP8eoɦu9+r_ M~QL_T1ZD<:q P`bfeyCF!Ey@x4ZDFf4kB&; Fި)ńwc^-/y y\))h^ɜhen1 ~ eU%UTT(}VRI?},7G(&Y gᴞ9z90y_+-v~Zk6j^sC#_2.livB?y.1\Jj*[S@Eqcf'_ПS=v ڤޯB~ZAVmu_bpDy jy;Y y=\y=C\0~T H(?ьUvko;jyuP |pΝZ?g"?7X2Irs1e nXvSR䋺TnCUh0 FMנ3:8=׌-2ᄙ̠ Tb4C?SX0n;d4ppz]Kt-[:zwշ:Od|cA]nlhbmF[ SF1aV2[V8e!.l$`C"dQZ+A[ 8 G00#aêKwÿ a0& 6Lw3 UaTmEL؛dIV.Di~Wϲ(,fi9WrQZ(8¸0" o0<#a8a:0G%x? ûax= T >_gӆ:D} # a;aH gߘK91,9lWJiu@ūR^Wpme}JҊJ\K Ԥ">͗_l)ĞY6]ãGnZa٪ ,;O9ʲ+cOހLoƦ`Oq+o˷lc. 5hŰܗoS-lUp. | {n _ul^/K2ww^mgiM_(OF{<ȫ#U0?kK@.,:?a<-mZk]AHD&%8/KK!~Z: jQaVanQD=|o\OMj4$&&aP& sA.K.#3h s8 cze!m<wH's'7yhƹn{’wkDm+rKMζpyep0n[VNpxıSr?{AF;"IvaܻKm;P6F#ДNLdBffeOH%-\lfev5ۥf]s{ד^P=ʿ:NL;s^ؖU%gLJkAoЦnju5{fcAxmUPwbߚ34߫m_wΝ屾[eWy[ǣXЏeyHခ #t():Ў8ZZl#ЊHbC/bxDž=55AԳԚ3fb͂Vmi Jfq:< R3l 50E3793p E0f?K0JiM鉇ٹzӝ%1n=uAx9سG)~1.(|Ox&\1c %KpbܵuP(pm5H(gC@٩ {?1} I 9tȇCA:GocKTpnN`6WώBR!5uhXs^#МRǘTt `Ҡ K}'#]٪hCң% {|sєŧ־h-+v8}_:V,e]EޓCCVWPc^KGZU?xd Rvᶱ+[Ȁ_+k![klCF0QtkFSph5MgnM#Jv^WQޯղc;5Ixibp%3sHZBd]v1 ?a(~)^2Nwp] A.ͭ;]wuGjahaB.Br`hL˶egKx)94f9i9$a*xRlvx4:UH|4UB\ =,&g#`dnvy_ɜ`1s,i?8R03}D?+f܃ݙz߂h!y3{N#e֡gcqvaGzSIEVTв4h5FjObÄ%~347?a` IVgCf6f7f9mYY,ɉwTgvɰ=`tr&YӴkGdǼ g9N)@=b=^uDq \F(w G[S_t/.&9M9=(;3dM."WSI@kNcn-77 ib+ן;{6WMVʎf,rio>V oR{7Jn__(ͼON:=E?Й xh÷I`qjatOoyo 8_1YSO£E@PNeGy^أd'9J׳l>)hU605?mf)$<t>Z\rY2YP:lwvlZ-dAYknrd, ݅ˊXUrHWѫо溣]oqwhn$MhU@_U@"{ GP]/ zCXUy#0-1ӣzaE%xV<1^rEYnF[rzDUȉ"nf%/<z ;0xC/6 z/~׽?Ujg4*xT%1P yRy b/xcP }KhFZ^ ^S յa){a#\c^iL'>o^{/L36c*M{Mfht!/Vg#{J*FE/ջ>u~7=$6'l;PP7$vj5ߌ[0f[l~ WF6#{;#=1Bg%W;{ObiU`DG/.Mb+1&ze$H I)rҜ2SrR\-ggWʨ9/ILjXzX˴kn 괯3*r'44q>KsnL8B!=(TF\TZ 堀z戋IY"6HКhlV4?1}m:[XVZu.6\.VܴI:S2Υ8[ΕiYQ1ݕNӥH$Yp&߫#|w%ƿ}Tg݉ȳ:ӕf#wr\Vu/h,&=Th}s#uZNϗ]}Fn[8Z[ԸGuVs,*,A,$ϯgVUg&`dA~ekr}kmM`0 n6}o4fe DE QU^dhjɡ-Ś8z/eeLhٵ+#N9ܷH[ 5Tb>P-CC<їq.u::nHWQ d,rZ=c"KL Xʍ"a9bZB(5LjwPx~az}X(_v|( BpKYHZ/4B&N7yGIR]S6tTqM x s3&6}ۣniVIԏ.:RS\_sg~b!N_oCzاt0;} 6mvֆ7y=[LsR춘jff4;qכ4g}|_`&OugBs'|@{BmSVגM#'8뗏h5;pMc9 \[e˘>NLurE2HL0CG7QzDXQKf ً0Iݛ!PR$ǎ }>"#90H T&&Mo\{Nu|Dak#S_SO3`L^?@y6ec }tWQ9)-GgubG Oh̰ðXWwVpt ZjKIIMRSSR꾐r6KSR~0suzN1 ХX`E+H5%U:@oH[k]W5Rd 5Z{b />;uԍXG{?w| 1'Fo_0Ƀќ`һ0$swP=Z.4y`Xݕnk<4|I{٩/!R׋ > i9gq^BM k,+H& c!y7>Cr5xa9֐.ǦFe cX'jH ֍e+gtqa9}zJ{"1t ?&:ss'J%]>OѰh06~dcz˼eB,Y/)RMlڽ:q-O`\Zk͂UL$DIrE$aJ4h #nҞy ;@L$A?6C)LzL'a#aɵ,X6=PMa3XIHNDKYD5 +NIړxIXKN:RJzşJF2' :AdJKJ[?qh|X;+±ضبV&/ܲE88>&b;ceˆ6mcK{ ]^*]N ܼvUltUUc0t{܊kK7Frh^Lnm{m:6 ʾx/"7c݌!+c;cy~-R֛DCܲ6k$ڸ W_68uKXg|8k8[zGclc;6lÈ?mصwh,t:[];޾8r岿4-:3%dlkH W/{:W_5B Cm#X&Q*al-Sq >YߝXedoPkJ,mC%<`}MF//o_]]ê#/nFcBb!u$qe+>Gf&xoM!,Qg )%9Q2N`cլdWGۦbd&q@olfJ%Ay$7TYnVi Yn:r~\ 6F0-_H/IL"8م>j붤T[Qȇ"!Ǜx@}2Ώ=q&f?<܀ctΖ<%G9Ln321Ƿûk6֧O1rۜrTx& +_WUǠ| 4)r!s2|eϕsE KʥK#&.i?S%(qbu;/\䔋H^hU-~_r.g lgtgO'?^:=ns絑&^3=gFpB&%_L@E/\Щ+8}O|$NsNMǟ8~8o<ϥs r!xLZKDiE^inJK܈Ғ_yn+7_Fw,.#!}Y^i$}Yyd7HkIDvEq1z!"W"t" b)v r!qGE/6qZԆ pgm+}3ƸymdweM\sWYqKe7+;=ٝ~L epEc;| |> `@mdm6KTD- 0/ecGTXqC8tbLpIr:*i_nV _+ endstream endobj 96 0 obj 11801 endobj 97 0 obj <> endobj 98 0 obj <> stream x]n0 M_0YDй>O Sծn_֝+Kx_'֒UQ<5-˂ YYo/sɵk?RK Xq#ĩD)s98զ?#/Ή*@>!r9?s-/ rɌ>2`.?##tbO!"7 "rBfROkx?³xG (,D(>9(n#\lOU endstream endobj 99 0 obj <> endobj 100 0 obj <> stream xԼ{`ǵ8GHwظfc_Aha ~ʏLFRZZGKW+u4?@\zтysZEG'W.VCZݫ\|i׮ۏGu']x*i oh?1Ҥ1xV' F1$-V=1)ّLMKw=̬ܼ| WLWV)'MZ˴3fΚ4g.͇oEvQʑ ݊,wx):PAPv={E/} -Cwv}:iF 432 D{]M[~DCYhيD@f9:mh>-}hM!DWux]]w%!.slX6B1yfCާxC&jެԴ475Ξ5aiOLf#7U׍UuكqoAD1Fl +fz.hu=Pe;nBSrs˂꡾qR2I's vnm~YI-'2i]nRV2na-͢ |YAh@54 L7Vhi+0-beZ>)%/4Ş斥-lDJ୤V1뽋' Xy+kX HA&(1.jZ8(8ᤖ]*<?$4NV̳ZpeI`!ǜ1{; FM_=]ͽQ >[Zd%xށ+遍 Hmmܥ5eF_R$[+k[ Zmd"B4>"_Lsg85$'b93q(lD>;lVDpȂ BeХBho ̡`{KXK2?Nn!⅐@{WGML G_mp<GBu~/ ( Wf?$8$=-a=I!F1HPbr (6m+I:EGt-ham&9ۨhm`lZk@tU,eAKlTޗ">8[z^0kgӴKx `scǢY_=5;vw:W8tf~T>) !S6/ ֧mO!xfᩨꭄXw@" @ӣ\s]4 IQ$ W I$ai52fi25k̊{}MqpDI#pvupJ=(! 8> -23 K*<0F!;%V U=ug^cRNm0mD+YMu#/[K?ӥOUE8 P( VetuTpmzԆp*C7b1='qnсOWÂΡó40Y$[SZM?uaHm"t(,BHE8a"t0^INw."a@u"4P-[kQ&?yN/vEki SŧD~H8FEnY#&Sf^ }.*T *zW#_v6:\t"GoHEqO2Hg pz"G`[ _0"LN^V$&"L'g9‘5Z6j  s2>6|!nAKRYaM5gu'^ǮqDы21b ArHP# J HHے7H֔VEΤ]H,M[AS8VGҔ9]4%qii D40ViphL`B* l}|}lyD ATޢPl$&h!;zĊ%35)Kn(kҕ:]*4]Ơ.:feQ"W&@OƄ] $WqK$9܂Ymsp#$DHZAKWxN$I`҃^0YvU .%8ځe%P؀JBk% Kp~^Sݾ趟,T2.MguvH*Fq#̋Ǖ|rBSMÛ#ERKۥH86 A[ቢ9RZR!UbTK8 4'֊Xd͕AP퀥0I &jysD׫M$|)`a.Cv>|o|ؘK#>χYն}xI|\bS9._21,FSǴMo?'~(#?p;N)͟?Kv~U;w nzˇ~X>5~b0_cÏS~xaǓ($ ?бܯB.od2XؘGuRfjkYƪkpH&I>}c~LУa>N[!B'!vEJ_ d'mGU('N?\V6#6>)?W姊_csdAÄN\ҹF-rF)-vML5Q5#Z9`s(':|v<^6䱦CRbRqI%Xopά ^g՝<ɗ ZN'$?zM+ѴN5i 8,T|ē4ٝRF)2479_`m.mkl+D)/RSxKO⬬Z% .RNM"۫͞yd*Tyf&P 6lR@1I9>~`2ƫCa! A?U l(.9TuHSDxHa&-y̻?s+7Vfk^՗vZ/+L CudI;gkSO'Za{f]1W1y~Ӕ39dVOr"hɑ^Y2x|cZ805w2w' U*]cct՞ePEt:ȥh&$`U]U{T qr̴P:ԡ<^Bs 9rnI ̘2<)VV%|]3G{T+V2/nM_buu`,4es6|aJNǣ,ݐ( 1 =CsY|R\@:lyil޺5 qfa8^P;aٌ _ &kpq1^J)u% G;B=]Pւ'U[d_dJ~)|O8ja1^èrBgeJrLN919sh:yDkRġip%ZFdbQ:k֤\ &c.2X2 E^6>lMUJw]bYj[l̡o֌eMk ax; ] . cg0aC UY0]a?~%̭Vu0C}Pf7}6sv„޴< ՛La({~&Gðuu0+sa0G{ Rae5Jl5IVSPdP]OlIxKܨܨTznt}u׷hV1׎X*ʘcn75'}&D]%e&zK({SI.e֭dύ-{E_H0kc=5FJXZ2ʠk+Skd* Q r.pgsq^fX{$Q/z?P[*>&R_5 cۢs4nFh"n,=8qVspEwy.*UMQұNjE$eIjN]`tpqN7dH&SW:S=Åc 2st$7{=S ՀPf-=؞Zr©Le׭_T3ojn"-kr" {wkO޽B_#RΉ^QI( p׺\ڣ yT3ېroqu_psnwtutj,-Z:Ӕ\yЎbJoH=ؗL /6EnЕ_}bV3sEaB[N.̜ܽnE|9aMϴnʼn3U.]ay~ٜ޶7 nNh]>1~­PS1B?0 `pAAE}UE;~O*9 nSJTAյG4Մ2wH!;Z& I`4A婀M%IإZjIץ$\N 8 t0Ӣ^-mݖC-Xtx6> @т,6 ~5V*f,*5NV 0P8Z6|p7wuu_cnE5%ףn ZY$ ֚I`κPUH17Ͽ^=fj(JBb7[Uk5ͮ Ī*jNJ?2_Q zMiZ[{dy"dCv tYYZZ6`+^0'vS㨳l(2UKɏTTV?F0DaNC``-~RRVR{pvb2Ȓt/'E㋙U嚸XhkY$ / Z_;q[ϟu)ioڊ֦Rnu/M5w3QWﲚkLwݶ2ij,'M28I1@mI/Y)UZzpggq.q▤$L'3fUl0AxA0AV)C6lH_&w-S\%V ~TR;m%'絛vjny)wz_u3rff'OO $=>yяą%/|{~wEN+t2݁>|2~VPC(%"J R3]CQ|b+"7@YA s $'U؟Hyhm~=`uƅ_a ˸&yFZ]b"4Hf>tKlkB{brtO!;$-zHS#KވajioPT!/^^r+eTLRzs(kc72o3hKW1qξ힘_C|Ezd$&ĸ;1I0.Stߎ^h2iUS_íZDd2H\QYSnA0$؝sz%hT!I&U/ _Vhˈ@\tjCFF2dPpCZFj4x+[[[`\Oͭ䞸m1޹`Swq޹Bږ)Y d,=@#LNhKG,1R#6d&yy8`һX0q+)Tfdf3Y*7u^yW5; ObO&nynmg+EvvʟceW_<}l9ߋ 1PfhUt{t]@[pVy4txb%>]Zx=rԏv#҂`!S_Ju!ᨀ$!cX ,nnI* uP9U#blfQf.5TeȻZ<ߞ邩?9|1IFΤ0@A q` `-"%FF'UU'v'+DK2n ].s4}CDͮ_U '?!ϝsq>BsTª"Bzn=H,Vު(Q -CrrB, s2^Iyu^hyHܪ4fc1gX`!C780`)[{WUlw5H^ٻ;}CIו9sR|3jC5}%i8ACWBq/+٤l $96&J4iivqBV)̐`K…0e@S˳ALJ)Ч(S) M5\g)00lOR3e4${Pl h2y`KVUk;\g){ՏƍwYlXo8}h+UmMO-\Cwq͢.{VatnRgf6繼rowyi= %DM˹?%GAcb Z%HCZ"/C-r \\%5DAqolcĿOpW'W~A[G'<f}O ow6A/^ׇ ^/n,f^n)y`.W;ƱQqUQe ZXɐêbǺl"{s6gdlZVZ k fC/?ceh͝l'XY< @b @1sP70g)3K#3A(L] d 8`HK[Pa v LphhN1hzG΀^էn q<I!P2HI|Xqk檯&Ή'ǜ;>h*NODC\{ iвOْ!e`)D;n؍Ⱋ\٢٫! L\;x9}vHfj"k/&8iQq0NljAӫ I\/7 mA{0*0N Pcq*)JqCa5z(:]u(f;W=vؔ8[=ZUo"waƌu(&o/Bf-ȶJ)JIY%}Fz0Hi$[U09=;O_vO^cI;>>¼Y<7dMtս0&ïyӯn7cokZzSW6D螞Lj̩5;KefrMɆw2e cB;'jpͬ=m[avAeLiWs%R)@Na( ̇2 o Dq6&O[[-M*&)9E2xdyر%cK*9)_˪Rd;TdꦹzꙦt_z4ґ1Zz`gMw5=/z&6g!z=t9[[+6M[5 <9#?<BjKEktQl"hRKZM/%uNKjBm ^3(?̓)ڠfR@a[ /Rs6US=j;%B13l^FtM;w`&e|=)I9V-vKl2qLjX.% A[ U]\ǃ+8vvA!#Oed3<ŀ  `fl*_N)qC9rcn<@Iff]ffyׇ/-<;e9i6ȫOF8тT=G9ӊ7MyR JRG)I̭z. ʣR%Ŧaqs IQ25 TwSP~BM 茲m}\޳݃#Sۓ$:hśI7ÛZ|NlY\։&\L j.=e`JW%vU+TJ=a&>4y}Yٴ]?պkZDvtI4u՗23mbL9rRֿy g,.h>r퓫=U[쪛*5|ICSwٛZvΌ5n75ffV*+Whv"Kb]mZlnކo)jz#@yPG]A'.?mkvc˓*=O1#~ Ͱλ͋׹&נ!t>$)Se1LKЪʊgXX1}n:lrj>ʭ;;f#yu2NNfh&]5VPLؾBj}nUL>onZl{m;DIncu+J:OzVډK#9ڥ5ݳ^ζ{SCΚǗ=v|@Ӧ)ZBck+.pȀDa؁:Qwj =F%[8U8r@6 [{@K4:=h9N$Y1v1PfF{0|d@^7~ d4lCaaA!z25v Hz"P]8A=9G=2ZLTfN+ΊpL'?~ >aӜV#o?4՝W^:Z"n m,[,1mK)4CBH=uN;f>IW,85[łニM A}X~J^g 0ljEd_)!C5d6)vuH<*ucgyc^jcfX#Gճ ZsiLd'#>uFq;ktޞlSeZrQCkRI)!#^xW+a<_ڢZxFya_kqu\ 5i$gDÁˎyiq3 :hl`~(檁GN.a@v PX#jQBm`&{‡>)Xc^#)`v$M 2/Y} !,`4 `SsD]GxX ˂)ZZU5VA\aaZ% `‚BMiHP `x[@D ̡sF N?FE+ydIp X A,UG~AS }055aYᚸcrSii@aGnچ`@? 1| T"e"D4>B_P"kSLr!:6)3(W@:!tYo- ,[:-C>h&t=".xJ^QXL'\;!ȏނtchL0a[+o^NVY&z j`aЀ{(sXѠ#HF L2QԉD P3.;90Նȣgߋ&N= 5P'>:\ S* GpUqS`Ci' Nt[aXݻĊ0ƑI$tt;\Z8\qe1f4lOֳijE<_xo+09Ǒ=*o5DZ dbÁ=p?ʃOyK<Ƣ].CkSѹIRhRD3DWk7Kv]sZ=1䀪?7MQ?8p38rwqsp9`qqA3(4q;`ZE(jZ}ׁ{hkF"Zv ;Hcb7xśRhU^/:xMR;})磫#f!< CXNyĢ cE>ЮSHT1So\6wctJF?7C?ލqumF\kM郔5.<5"@[U.i$w0Ee&BA5Z]S#6%@}B[ c$%!?< q—tYbH4qd^Nt8tlwe\*paW͊ρuq6'|( 4p.x`o:m<#7p<@#5l %w (!V"DҙJMv46i&EudGLɤX$IΑF[U_ʺlrzBLnO\ rL? O?Aehʸ5 :9Y%d~,+G2NU 2<$?%@&en)!_ڣ^/ee8-.$C(jTU&y2`a ޑA}Me8,mw e'C dɐ*A+2|"eX:a +ea AJi2d=|pNqa/]m*t 28ee(ZK2| û2"ó26߯6_/ɸLEǡTtb?~~XvK!og A%X^@L$G1%'-$D!!'C0 S;.w ]U;}W_u.cS_.=Pa|J1k<"|lo&\v܄?{Q^Dh9(!>ѐl7G.h0II1 4ӗo-K%e%+[ٿj5S+'$WH*L\[txE} y|ah  ?ݩ70>96r#X2 ZЂvu,D%` 0?PBy^l7q/v:iWT}փO7\Ʒ+yRϵq92I7K'kgk11Nꫨ.ΈD,n<@y=vwB;}kӌJ⓴)*)~?]Aoע.q'ڽQGݎS* =: b5x]뵡#"/rH2a,-umZ\]%L[46M=͇- * (f[D};+t,MYv~-ے6%[c',ħ# /lؒ1BLK6Z* 0MKBHS{pw[&)}[cCݭT={_鞞3dg0cQx7nj!sY5gmDɔcfQ4dLN! es,8:՚YD 6Ա[v6Fǖm;f7qVBVRYY'c/ c[[y{v/{ א>m.\6.6dnXLPCeJN)&T+]F$ @L$)}f,4ޜ +*\O-L|-`_6* 9Y|!|[ qCm^öܕxՂ..-8\ѰmipP;~wҵyUuTHɎ伮̇H=Yn&]%_x&6"~#^f$B'FH=V2bd:]Gwoo(ߧx"KNE3/cJNS|PRM,g7 (ɠXx|^UzRIf(/P͉e:L{ JPNq6řő(%~GnPnf2I >` scTWF=<_RAؾ$ZM()ǀUhg,s&@3,K襺+sJRtP'%HxPX)9jĊ*C|oeىYSџ@UGܸ19ń2)C|!$5BXI:+>,Ff:woEM>3x/6< C _}YWC=/!G^_M x`?ow \T)0uC)w]pAX!g!~!8<( :@V/ g`S,^Y8^d"s|4 ? r*'KLd/OY9 h؎>DX"NnA5ƨSkZm:%R;SvrJv2\3F=9'+Y$+${{NeǹrMO܌gg\7^ͯW {Y~Y/d}}ޢyE(^Z|dc0~UzqY|E'"Y&3+CM&_nKĘ&Ѵjr Ndva\ e(K Oh !㏶l=rmS>F'#At&;%,zCcFc-|X[,}/ڤƑbi953>Jϰ3oW4|uxdX W,{sU'?|W0!Y>>" w_㷇H'bilB W:>_{>vO=8-XN}oě&6Zθό$J\NSSI2tIp#O~?/zeT OH]L^@_}}d_Gjk|.ctdY.[R)](>CC7AZ#ǽ҆5z2|:|1[@FVM"U +Us< uxWj@NyUt{q+˗->#0’dq[-]bqZj-1Kebpn":> endobj 103 0 obj <> stream x]͎@<a^ɲגQy c)ك>S]DVM=u1t;C=%C711?k?d].LYjOo2Y==/#v9~ʊs~O?v>}LӯxÒfwL_[,إxN%S̭ Qڱi טrMg%KSIe&i+:ЎB{tm%bAע􊺂~]{-u 9wǸg<@1.9jMɜhW{hWoi_ӀߖF4kp'Z[ٓ{9vM~ =|3wђ?/BIn wK+xh%N~~'fK V%[e$uwđCG~=ޑ##}__K\V?uw`vʞ֫6O^ѣ'W<+k+W?ޣ'oV_zgP~p[~E+zO @/A|N̷?c)o?9$20!8J~7 endstream endobj 104 0 obj <> endobj 105 0 obj <> stream xY{x[ŕ?sC-[R{lɖ$N!ŎI,V*'bɱClKNڴ &iB7P | 6M#R $P>ۖh?v i--̕M~z=s̙3sd40 ,ȃq? ; Wd ~]C=@p$*Pad>`뷊| m:<iT:r*#3+[\p;|Xܯq+ v̿KkWOҊLN̤ ~ ^#|އg8 箖&D Gx w}OCJ_u%+?N9D6B(}XKwEރd%ccƈ~q̿#+r?5CY{Gm`30?$+pM0_L+3 > !r-}agig8E3'.eǙSL0y ^!br; KAnT @|F+1WҋA8Dڈwamq}= #do_0۳ߵn k:Wm-ͲgUS ˗-u:j+JK60ϠisY*c^7 &\^CR Yke"&^+)$唤|YFhxM ۤ>1qA*4WTrba[8&&ȀMv xP߬:UjdTlI5R iQ^Ee 3`KpYmꎄVjSUQP&25gÖ&,C7lΰޙ= =Q)%*o}gITKmބj\y+C_ę#]xZN(Q?Jޙ$ffBs[$Q/j43^DA57քo__B?0LV'[ߙ0vo &R8By$rз K̀@ F'~ ["liK0BK~L/\> 7;{3 #,yC-Oۨ+$}B{M5 >EVD:#b/CX0RhR>` bjH.DbMuݞr}o0!!!>:Gh]4Ҧ/yReR#=AK["5^ ,zgR&P]RwIpϟ]"Zsk¦V2L0<a\iCbjK}>)飁Ulʈ 7#uvo .OjRHAkJ \"4S 2V}HH-xOdfb# jK f$*Eo--G(i8/hS*iml_M5bz`IAm_hbK1 A5 bYHc^ JO?HFQPN`U5B  fg^ nbR\mXsB8)uPRZ!R !,/7XO׳ "YYky.#<#i [ocB'md2+ݳ2۳1c{{'´͖`[I@V R&B5J"o}RVZ9(9&/q)T8ca}3}4Ii#%JȖ"- B* &RS}+}XXCFx,g q5_k<2HnS Ɠf1bܟF"rɀFZ9b*S" Yrc)!y aU&WbEGUPPQSTw;ݹ MNbUD?< { $?G83l3ÑSo*85 (Y qx6UOg GlY T_py.gHc%yYW v"ջV͐W̸]KkYi'_;Smu<)Xaw~Q4$>}齒yJ\mk{+/9[?Yynlق`3eMEz.+<7q*6R6k|Sn+f ܫXT58 ؓh*Si5jo药[g& &0,o]_hZִjQÍm]A#fV:N/"|f, S'BTہ6ǛV4"_˳ҹrؾTmc^/k87"vy3w_pI?/YxH=ΊN0TU~ e咖5fh<۵'OmC F~ٲ’B>i+ᅭa把mlբY`kZwequ.%ܲ]|Fzr[+UUpյbZjmX~Ckb2b!zUhbeTzVZMA=JQ4  0]LO=,3~dzӲgo/4ߞC,٫w}3wyA3lr&kCEA{rP9ZL4l}RAAm/-V 0l2tl)e;u%ImiGk/"AYQe&&Ywӻ/-nlZ3GrWǿw{*C;ϻ'3+~cOfdRD;[ 򕊬+P偊abx&8>Yn!axaE0D3~W)_-"aroטq>}ߋYX"Hmq#Ky31kuٕa- nֿѹ<Ԝ՘C÷R_E"v!ĄmR,Y8]L|i0ʜCrvh>OF4Dc4DVAG:ӊFl.-iu9˅lp0#1#w?H$\%ĺ$Ud%KU E}wOգx^qϳTj~N^Ybeei5B[q˵;W2Rg~AmI 7PtzU|Z'CyIAS[RAt rSA~j,O3JC4zXyivGwn߳jwؓ=8[NF\fR V144FS s(dZ\25'WjϷpSB6A Q1k7,,I~@xtDg'e>[Zemr s"e ʹp\3p՚!ʩ. 0t`[dK*Xۀҭmӱhј9![t<2euV ؍.c2 gu4ڞh)E0EIiBCF$i=)\Bv*俑H&QUj"{Ъ;oaKx[s˦KO}vcr.낕p\ZAlfWU1k\ku{vBB`:~;%UDrV1UU%z׫E5V*;>EPVjiiˊY%Vd!d8XzSrߎmTꩯiZ\59,7ƿ K2J}Upe֦{nu?,u,/V[J=X+Pc[=R.VKuz{9ዿ]VY!4+_ &qJ-^zO0}o\ t; {0SA.إWç5RA2̝̏$;~[ק ~#j2i;Ѓ6wqںll|.q-i8J,{4y0y̩K* i:nGt&!MgӴmhO90ENZ3b. kL}&P<d^N,b^MT4C!ۜUP JәPt,i5,Hs wi-lPul P<$>*jkͱX82!F'ƣHt!6o.NlĉH,2#v0%jFǢ-˝kďz e9j]WisǺĐ#萸moūŎAu$L sdL 8z?XXܱkhhd00# GhɉXxds\xdGD\#p<>ܹs#DY`t#Hld1E|8ǫf<EcѡD?6e[d0.ƣ(<ưkhD$2Jg:XsxdpXC8"Bf_3vN:[ı܊K?ca\q,"<x-Cb02$1V'&EaӸ+= ;%9D"WzR|hcTuF?E䠢<9pmL(~ skD(P\J$жa6'^;ˁ2jLu((J⸕+8F_oEٔQf@P,R~'E>%!W ![ɈM2KBhoN^NŞA8(>qU6;nd_g+*KJ"\'[o}ys1zzw70O;4o~TlħI%=4'ȯޱz$4%~I>s /ŷ o?;_!zMl5v^0G^>2k-k }svFarFAw A*tps)>7?K|c|lcu p,'H\QQQ*#̗ Cm֒XX4OBا "EDd>@b0E}oL7[ Vԅ), *3}KJ]:A빗"CCCSxH5Gr}?|\8ϋn7#8)b@ `0dr}]{O,qg#V!LBK̟+@\tfݔ霺.]Tw@n^AEgi=v{\DSMлܽ1ڛMYB>3 &:a$dJL#/5AK_,Oڕq{ vVLSRB]iT5fCX,ONNʠIlC iSrXb cj*TشF[&_ bT*V߇K endstream endobj 106 0 obj 6259 endobj 107 0 obj <> endobj 108 0 obj <> stream x]Mo0 CERGatFBz/M8~_DZӪ97FMmy9 6Șߣcg4xe06f"IBnnazxHise q{ F0,!ys7B]F:XXP+rR0Nu]&`Կ8gT)yU"79q[yGES !rV#D##q"=G>G iv5sYP[S/~F͹0ָ8O6k;YtлX endstream endobj 109 0 obj <> endobj 110 0 obj <> stream xY}P[Wv?>} '!@{B > H$r6F$#$ fn:&Iag8_$mvg4nn;N;n'$;mIs=s{gx2-H7D?<_kj@7FwL@@.1~w @H{w,٭~ Ģ1,߾*Cޏ|d|QIT'ϝ 2?|dh_)A9dXzɇmH,C̷==:~taH{PF#\iH2LPTSxLL}d `/ [/="y{ބ 1$`߂pŻ_pހoI4,EP(|D{v&a K~P(bO)YS2L$, Sy pAACf#[a;f8Hgl#R{m~}Q/@{7l.l,h'g}<_+{7yx?3Z[zKpfאIiz=1y ى>; Fk~78 lzhcG6ܴn\װvMښJVTh-p&j*B.JJk-ج~[C(#&xIAQ_Ӄ_$5=w4 7@CE9򉟵X-~>qS7&2X,؃Z ބofllA{jU9(yI5Rkt4%5w1aS $}_QޖX[&hM&d h\|܉Hh[_ a9;7w$'J-pDś Vۻ~wHV~ Xo޸_JIdE >wng}s쐕gsiisQ/" >쵰qsw?Țd}]퉌έ} ZB(jYmWtU3 bj?!d}I!E8 ZWZ2{ٕ;݃V\mECO; ͯ^;E]jR‚퀑"tcEF|vVo03<'/iAJw҉=BA\qNk4a6YO-xw%-ahN@p8+#޹`K ϯ͗\ [ec3ƕ;72̴QlIxq}~!8E1A{ڻ[VI6$Eޯf0"GL?*(}HXNȋXX\ 3hRnI }FB85W4͖~K(̧ TJS;(E&!>ko@07"橵깏, ,ؼ`&|v&ZEJsJ3?w ƭ)%@ajY~!&1fscBYF} 6 c顝4Uf4o%G;=h+x]6 QJ ^`K](D}>V(L"#0@2vEFQ&Ik}Zzfm1&qȹ]n=KW hNkmIׯx!X+7-D#` P[5`!rH\L\DUɻq+i[Zz3~>JӔU:+.iO-oyZjTJnM=޳,}C9uDioKn.s/<0[*7YG] ՙ6Ŀx-t+P|WkZ_MwJΟTjzR_j-(kۍmhd$FmW,cMl: lX`G$]c6kH],jY-1E$] |36UM}P;#h6qL.KF3V\ɕm:xmt=t KLk;+הT/f+z}.hoKs[o;Lz{%s zmp5tB.(鸙!rH8VRq]w& f6ϢYY-6UϒU'u8ej! ޾~{ͿM Vb.a"@^+L|&FƷ]-yx׌jcӮƺ]=d;`HAC|\z&qanխ/PJWҋ]y:fZ3!繇ՖoB'y,™*Iu節[ K:֠ӱ0J]TtrLfe2<61&.TbFəl A)#at 'գaps|L|%Cj}$u!">p&f LҧѫDF>˟)cOl'k϶SJRNJ+ѡ"39RYI!~,Ṁ6"B~.O9@C"Tf2ZnVŒH`y2-I%KȒg#TsfG报Β,2HK,rz򫗗4жO6\]C UZJ>13/?[B#2Sb`?y _Go>sI$ ad~es+6Szwz4mx4dA_b63$=3|J(nLN`.1YxZs/!Zh 2ؔA):'ST/x,Q"7KץhM!hL(gJSLLoA39\ɥՒwRtlSt:,IS6B-;#H(_++HxoLG#ӡxdwct8 8:Ƈv~cd*M^O*3=F9Hw<<7p,25G8{uRèL:_[|4<;0FXd47433< Ox vLÓLGp4esvN'Shq؁%q3ౄ!j|:C Qg 50f=*!F[~x ǰ-&ra }gı0CnGcgYԋѪB_+XPm\A6.s4BYFJ_Y.z6)wVh5ES*.6%j<`M8oaѶ0c)wqUc)[[ GFH݌8FQ#Ch(;W8P~)Dm8G>,hJC9+FX$k<*cb8zjec",FRvzLF OBoaVt=ƚǹDM3,ڈȎcF|<6"P}+W}鋟)ȭoߢ?E^Evz#aFˍߐ+ '~BuRYiyY6|N1Ǐ|#gẏ{O\wLHK2L 9pyrdX=WqKK.1Wp8ĉH眯_y92]|ݗ%Kw/+I%k__BB~NVn a)2&Z&L.cͫ?zF |SzicE=eV*|ӼiϽr9s.9gĵ>it>~j_lyxz{{ > endobj 113 0 obj <> stream x]n <a궉1nkRP-Ix m ao23k*ثռGGE`=›Ͻ!{mq0jUE؛-npz^+Dj̠MH]Sԛ~c+|Z-0Nc+\ XLjR%IM (/a䟽K$=ge|= \DNOϑ] ru&nBa?k|֯1oE: endstream endobj 114 0 obj <> endobj 115 0 obj <> stream xռy\[׵/s4G! !&<`@6` ؀`OIlxqb;C n&i'N34v3mrۦM{oƽmڸm޶ [H`'>>9k}Zhjbzi Hܰ| [aS[Ҡ|!R54qkfǿEKFHoܲkvpMfx=DBF0TZp6uj~M wWmD(Rg?COWp:x}П<&Pz~A8 02vL( Jh2[${#rzҼYپ<~Aa&(.o- 7.kZbesuUÿſnI| dF;!; U4:Sw=g.DϠ+t?h"sTSh DOs^F,Mxv;Ͻ]~Cc%ЇNfؿGXԠQeڀr~=:a# ϴNC(M|GH}dmCݨ5~9M0# q Q?7z*:= Yn@$G(Z֣ bCgG{۪֖H˛5ԊʊҒ|^nNfF7͓vAӪUJ\s` tO8ˎ=PSEߜ Bf']"\9+ؕXpUW5vuwt@zOkT^!t@ n7j ׻pa.}x;VyU9J E52=qf $jSsn<^pFΆPkӜcxC~]fw:lG.h=ʹtĎ]h9$}sp,YlZ1;y< @#s3A61Vx9nϬ*wJ׺W#9>w|H eVt}] FWa4xۇmЀ+7g.싱cNQ|?,7ϙ=Kɺ0C%~ۜnm5ogOv5Ǻt\DWPg'ԁ\7v 90ӆ\v t v2A e]ǹ'ΑUxGb'Xssx:f@^i'\(@+ϒ*ɽ  TDuIЍ,W`}:vFy&Nudک ݝ'7iWp5pzA@f*FKyWgv͉66FqbH43G7 Ȅpzs.LܹasNfUqOA=oCL2]l>{B0aFKy(<̦q`ֳJ4mYFԄږ2[rރl9/;Wuu\voI48qх(V*فZ@!]o("4# x:BF.XPGՉRl@c Ɵ[;g:9xjc"̩ꃱzd{Vh|l˕;(YczN%_-siO#8B萸vZv@F]xʾN'L";,,dG¡2›)`> ;F).)`(8j24PhN8mW~^A1@ Q'P\O3jh0X:N9e,, /(ű6|ef'NθgU)TuV\fn7L('ʱX)';,"[pSQwYytG֡,ŋxhqRV]rrѠE\.4jMbN2%TwT>RQTDU8$䤧{&s$/ ]SSODAg*#rFū1%ϐr: g(Mr0pq䤸(ݓ*DnI\SpsS^P~][G6 vWc}Ց[qfK86gvFC%i{vw'L, nڱ{L3۝[w{cբSVoAa^#]2WJʎӠl^3&pCܤkfLi4X-a`R psu,Ml8`XMϼ0?An+Q Fwݟy7рrQq݈2k4M$U©hMs;\.hrZ,yk*̘Lb,N\Ic83SHtˀ e|e]fd| `sm_#^|xLn`DI-c_}M0Md]^[lSG #Om+*d6Uz|ƪ}BLoħ@0̙4ToMg)Mf ;靔nЍ;R҆RRW,%M$ٞcS˨ٌFy{zq8U M"ۤ[,zy$#U6!N=fD$1er3S`dEy7C Ohpbr|w/Co_>38|f swl]5Z20nVc3ф2[K\8dXm !j-iwI#Dve1;MPJ)6Gٜ2LܜٛI2U5DToអ=?8̔K+WTׯ%JM؁C$cKfo7mhc 1FcDꋙUZ(-_,wԄ#Yj1æ%E' =i={4qXw6){uM+Q@LR(x1FӬ*<`=P07`EA `|GG>Fn%# TJ]Jq(yu2lÍNlčN#AK  'PA&-E*D3'HpC n JJ0tJ8mo_$֎6Gk"$jǰ'F`36͋Ȃ}Sŝw/o|5g QXɶ6kL.,^J:CD7$ua4kEn0WĔ)Š$epAS;ؗ7D)J~rTN&NqKωƄ0܇1 bPzhmcl Mg*v[0lS:iGc:tL1A]3y'Oxފ!50' 0K@G!pkku|־w#g϶o?_?yx޴4фV "ȈL@|aIDdSC2;7ۑ1wylb>?0{XzL3=Yŋ($J9BpB 28 *UҠ~~~οc~Nn0-e1 >}~.3lO ;AcP hD`h/ƶ3Į$VBBKΥ[^SO<:pҎ^)yd`hŦ[ו>6~It wuNlMA6::̥ՔH%!(iFIGClv m:ۈm0(4ͩPRhv:F*և lvQ6AH15cF|=S@c9FI8>U2H+ b)ZCKM odΕ.SH1}+Ԯ+qt~yER] lOmōYY#Y13L;8ȎCdN٭ՒzFNp=SQ*7b!IOݑ$,RBf .TƼ8~YwKҗo[VڳP nzpxk{ʼn@ԛ7hz6'Tv7Gt+k;DzYb'ӸˠRCx_hwL{0E*3^,^JoB<7a_Þ š7?4\Wsy]-ΤmvO9oDMigD(8^ &Xg`12 p`\X0+#WHziL(MKVͬ%/E6VrokJm˗9fbGX_5Wt@bûdo*wk HZ)v`MwY\$1#-g0r9ǝ3NTWqax&O( X Kk *Ywbb񔦸i}yņpVcRqφw W+' ;eK2'.ќH@%(C*ApT{UT*& ^lnb)Fs7*W7ִ\[F0ʞu*'״Omɦ(';yljcVSW䬟San 4/"s2/J9@%2#)7%ƈʨL4Z2 P3R1~C`[XO.R6~{xd 1goTrIl7-OƉZC(m&Y`h1KBаnfJ414uSk_[W1ڼf*{?>oы(+aׅJm8<6/|~sS2QFZAKi^ԒwkbY$152$5ٶm"nm& |Hf1sg5vDu1,n+>ŮZ|&sGXVq9cioXezjn.BE_\"Pmmt{6{h18ZnwOf!ӌ#I) i-Y3g]nh-,f= HɀL.XT%{u|B(_e9\zJwɒM#>MR~z}RiSh,_ﺞ@0 C3EO݇"}6|^|6vo̖0^EvA= Rf pAA_jFccثXi֌i19 h?}!&T0gXe=I|c2Xx9BkWn63〕?ʭ6e-Y[UQtMNdה$$tu 7g-+>Y!GET=/o<; #Lr%BHT21 sa40sS zx?IZ-aʇVyoj2N*V5$?>gI>z8?B`[L`\t]Ǘrx]08Qѩ *SL *^QxP%n1&%1+[&y$GJfRVkyU`$Q g1f FkqgM\,b"Z WJQO^M|*:mߌN'+zCtn? ƭ0?RV"Ll<`@9DHpd87'ߒ|$:s:b?b'vZ1&IQZ֜5^mvZ{@aqލ)b׌m^ňR B^guZܚ\5˵:gJ_}?M53&c]VQk[2b*NMObQӲ9'3f&'g{4Wf\!AոꊊSD+03ƖYnT|l z$7NM,*LfqEfkSɭS Zqwg[sok:9s * /`.ٵ6>зcs>[g92 q@R-]fԉ#nEZN.3eZNc5R2m_7>kضj[7gܲq~̺.Oa$yE%0PNt8?̝N8Hib0:B̛ )x5P 3I_S7IɆ3ƥPߚc9r]펃R&XҊwH p? u%bpں#*,E6z% mkpV'1HkoK;^]GĶ.ot2̻otʁ݈32TslYٌew{Ǽ1W`stDUdawFofK¶^zεx1#6ق̈oX8 q+z<}b;81d{&Xמfϙ JrSFC_=UԹltWáo!k5Q#M[rL,bKO9E.wJ! %S(K|BhWĝd w^{]b\TÀT4WvW1)>%׋2lb}Lly2p4p5{| MXF~ Iu33Nyn$َsa[rċ/6O CX mߏŦ5^6dW21q^|RN5<&yP̘`$DOcM~ ίĕ#XcN6o1̦Uf&7Rs535D7)ljzQm rsgr`gE0)T MMMɜkI-H%e- َ1_|! r^zzxno`ϭf ˥l^ {:Z^Wtʭ\[45;ίo~Yvv|OҚ_fRV}ٖ+f4[m:}KXZUxI鶊Ȋ=[\˽5[[<-C5;u%Z5e$$eNK?Ϧ0X>ÔGpKDUhPġՕd7:}D=`:Wƕ ,eᬬ@!3ER`/M)aJպ%k-X]gY*IJ2dD`df􁵙_U7V77L$@9'THyh'O3Uf+*hy5ro8Gsd]$3(B<$ۍ]Os,%LF 3H``1BvKv>}}8J@fGNt"`&>6YtLzSd gVr%r6>"dVY3Vf=kK R[LEdTl획e6LUnAQeRiiXsDـ cSZڬAk&?ުarg!X:m`i;p޼16Oq6)@z>V/;FWG6 ;G;ю̋ xS%:ޭn[bZGȋR\̐2VC,j]1:⢇8aKs5_Fk*u‰gcRAbёq"$9>-K)Qfozo% %R<%?^DT=|wI\b8#;Ze 9 5+TJU,+'0!r^GfB 9SSfV1(TcZަJsV!aRRQ) úPb-s_=R! 葷{ozo7E:<tP"YMFq +Og*0/.jxB.NB.*( 2\ H9̞@`̔0g G(ڙ#oM~oSo&_$F0%+QP,EQA9YALiurYrƕ0bqYA~%R ER Ky0>b}!/[pJi1.OX>6(M4*#> .df,g#ehcK-l홴N9(fftӚSZ2֧Dϟƹg*E[A+M`;-`0/BCł`@ l@:kJ鬇Ez<&ZM,V/(Uh5h`猙1XC&v_PIPy.`Qb3(AnMMJeaTn-+DB[Iu&I$"ҟڜڛJSS LԗpZN2U`n.?-h3x8gYJTZ\l`-uz`0z^ݪP<'r^'zn 傀e*lU|2Z" Z{kAyQ]UjǢ*u%n!% +Bk2n5:`Rˮ7ip%Ũ iO,y+(1ʩy!߳3XbI9oy ESx5t'd<ӽ69~3'4Q 1]1 ZWbTw Ar(=(PY< u鷣oC>砝w8Qw":Up?5Y6]"AR_R>Dj\-uF<~UnAeW !A4͘ob0e6/[R-/$$$%%IWkvDEb[ꤟW5ɑ:Jw:ꈦLSW5wk2 h \j:v6^䉸H HxsLQZkexYtbe9ڍȌMpF>/anh/^hS|8z)hd32A:x"xC)T/F3eJ&V?xY2xYq^EQ~MZ-֏l=8wq痺j'7  N&&FF\[&F6OM&''-Y?;Z16::qzK]Ճ\_x.t&Olv ZW<Ƒɩ usEG\F6 J'Ⱪa鉑Ɂ iykVOM NOMW;v_0M8 ں}rgjxӈƠcCS;''o0k][`pkƉlRw lvvo08>aQg,$գPy#xF` 䂭tBPE(\1ͅ|K~18']\Ps |{K7K57wj3AmҙQ>o[h6%a=*z3ԍwIa Kw@ kB)keJj;vv;\J*"(0%=mTo<83zݸr6m1(i xyRi+\{ǃII GΓ R.I-b|<';'? 4\Zˮh'v7Oo?`9vq7yה7t>Q?+()7?d:tW(y-TGKa927wy9?&4 l- vyˀ}pׯ`sTкsm ]m*4sVhr)bx X%zֈQ"of~9^r8Bag]x:m)lBG!2 vַ vAv:A}~f}u<u€O'`_Ϸ.ȯ6)#s9*[dwΡ> endobj 118 0 obj <> stream x]Mn0Ne,b;DjYo_yl tIPP4vwxii*O]ߎzM*GNʶk|eq(s~yߟ.e>?N|XX|4vGcsaΩʪX6y>/sZX㾝wq.7>Rڑ\tMG*U*ݪH}߳'eynZUޭ,̂=2+r`5s#33B~\[ye!2mg9*˂y-2_`p>5r~E?<d#ї~/pJc/G菖VK#G+G| _G_У-`NzNOW{= _1_M= l~w=/Zx ُ}iZKGK__Wky`M4m|?kX[~ُ^?b C^W1k;/Gή?GNq>n쀳s'LקgpPe0 endstream endobj 119 0 obj <> endobj 120 0 obj <> stream xUoU?wfvvKMVNnmy& E/(3v;B 4Fk0!A~GPA _@LL`3 &n9;;733 Cg6H%b gI''?$>HN촒_c(3լ<@Qt<5{0Ohs'E>4* WCmLv=1mV_5t*9Jݍ3,=%Ovů?<< oA7\"G`hu> Sp ^o@'#ai/_?m[#;e[,mil$BUf.Ez]6s9b5R9hJMU5uR6BgCwr_X]' /`,>b9 D+=WgU Ҏh3SnPryUFÀ^`ѐg ,RwZGt"4aZ9FuzhI-q+ciZ ]V+8?;k`NWiLL7գD4:#kT z'ԭ4Z.hPJq )wp2QN rqm#a.O+IZ6*5N0{Z?)ܐȷPj\ዧWTHӚ՛6nвr}}ƞsyod.':OSFA%6:IU_3M6}>\XQm뿿&o=̞,ΆLICqpb0d:Vu9D^)k-/XYX+ }!{KF<%L ,A&J+/ X*a |R- A`8,#&PAbc?dX` 3.peR%4e %%C`?t(~+Ie'9fgioٛfЄ}4d|sCX&kvϻ=t*1ӣVv13ϰl(A`ȎmLЕr9\Yx>X*cY:_;0D`|O*c)$h(JX>cM[/Snޱg2v$Vf6ct%zƒu@jo!ȃ~0dar'H 4̘Qd&a= TH 1@O-mގB\ܑR'sOsrzyŀ^$y>/AgG l endstream endobj 121 0 obj 1401 endobj 122 0 obj <> endobj 123 0 obj <> stream x]j0 ~ Cq]atr:[ m琷`%ylh;0縐EqA o'd[S4B^7gZab{!|auXwKJ?8aP sM0BZm#T=d, #44ܿ` f\R/{8 ۊd`"NUPlA|S6o endstream endobj 124 0 obj <> endobj 125 0 obj <> endobj 126 0 obj <> endobj 1 0 obj <>/Contents 2 0 R>> endobj 4 0 obj <>/Contents 5 0 R>> endobj 7 0 obj <>/Contents 8 0 R>> endobj 10 0 obj <>/Contents 11 0 R>> endobj 13 0 obj <>/Contents 14 0 R>> endobj 16 0 obj <>/Contents 17 0 R>> endobj 19 0 obj <>/Contents 20 0 R>> endobj 22 0 obj <>/Contents 23 0 R>> endobj 25 0 obj <>/Contents 26 0 R>> endobj 28 0 obj <>/Contents 29 0 R>> endobj 31 0 obj <>/Contents 32 0 R>> endobj 34 0 obj <>/Contents 35 0 R>> endobj 37 0 obj <>/Contents 38 0 R>> endobj 40 0 obj <>/Contents 41 0 R>> endobj 43 0 obj <>/Contents 44 0 R>> endobj 46 0 obj <>/Contents 47 0 R>> endobj 49 0 obj <>/Contents 50 0 R>> endobj 52 0 obj <>/Contents 53 0 R>> endobj 55 0 obj <>/Contents 56 0 R>> endobj 58 0 obj <>/Contents 59 0 R>> endobj 61 0 obj <>/Contents 62 0 R>> endobj 64 0 obj <>/Contents 65 0 R>> endobj 67 0 obj <>/Contents 68 0 R>> endobj 70 0 obj <>/Contents 71 0 R>> endobj 73 0 obj <>/Contents 74 0 R>> endobj 76 0 obj <>/Contents 77 0 R>> endobj 79 0 obj <>/Contents 80 0 R>> endobj 127 0 obj <> endobj 128 0 obj < /Dest[4 0 R/XYZ 78.3 745.4 0]/Parent 127 0 R/Next 133 0 R>> endobj 129 0 obj < /Dest[4 0 R/XYZ 85.5 655.4 0]/Parent 128 0 R/Next 130 0 R>> endobj 130 0 obj < /Dest[7 0 R/XYZ 85.5 745.3 0]/Parent 128 0 R/Prev 129 0 R/Next 131 0 R>> endobj 131 0 obj < /Dest[10 0 R/XYZ 85.5 745.3 0]/Parent 128 0 R/Prev 130 0 R/Next 132 0 R>> endobj 132 0 obj < /Dest[13 0 R/XYZ 85.5 745.3 0]/Parent 128 0 R/Prev 131 0 R>> endobj 133 0 obj < /Dest[16 0 R/XYZ 78.3 745.4 0]/Parent 127 0 R/Prev 128 0 R/Next 153 0 R>> endobj 134 0 obj < /Dest[16 0 R/XYZ 85.5 661.4 0]/Parent 133 0 R/Next 135 0 R>> endobj 135 0 obj < /Dest[22 0 R/XYZ 85.5 745.3 0]/Parent 133 0 R/Prev 134 0 R/Next 136 0 R>> endobj 136 0 obj < /Dest[25 0 R/XYZ 85.5 745.3 0]/Parent 133 0 R/Prev 135 0 R/Next 137 0 R>> endobj 137 0 obj < /Dest[28 0 R/XYZ 85.5 745.3 0]/Parent 133 0 R/Prev 136 0 R/Next 138 0 R>> endobj 138 0 obj < /Dest[31 0 R/XYZ 85.5 745.3 0]/Parent 133 0 R/Prev 137 0 R/Next 139 0 R>> endobj 139 0 obj < /Dest[34 0 R/XYZ 85.5 745.3 0]/Parent 133 0 R/Prev 138 0 R/Next 144 0 R>> endobj 140 0 obj < /Dest[34 0 R/XYZ 92.7 711.2 0]/Parent 139 0 R/Next 141 0 R>> endobj 141 0 obj < /Dest[37 0 R/XYZ 92.7 684.1 0]/Parent 139 0 R/Prev 140 0 R/Next 142 0 R>> endobj 142 0 obj < /Dest[37 0 R/XYZ 92.7 335.6 0]/Parent 139 0 R/Prev 141 0 R/Next 143 0 R>> endobj 143 0 obj < /Dest[40 0 R/XYZ 92.7 745.3 0]/Parent 139 0 R/Prev 142 0 R>> endobj 144 0 obj < /Dest[43 0 R/XYZ 85.5 745.3 0]/Parent 133 0 R/Prev 139 0 R/Next 148 0 R>> endobj 145 0 obj < /Dest[43 0 R/XYZ 92.7 638 0]/Parent 144 0 R/Next 146 0 R>> endobj 146 0 obj < /Dest[43 0 R/XYZ 92.7 386.7 0]/Parent 144 0 R/Prev 145 0 R/Next 147 0 R>> endobj 147 0 obj < /Dest[43 0 R/XYZ 92.7 251.8 0]/Parent 144 0 R/Prev 146 0 R>> endobj 148 0 obj < /Dest[46 0 R/XYZ 85.5 745.3 0]/Parent 133 0 R/Prev 144 0 R/Next 149 0 R>> endobj 149 0 obj < /Dest[49 0 R/XYZ 85.5 745.3 0]/Parent 133 0 R/Prev 148 0 R/Next 150 0 R>> endobj 150 0 obj < /Dest[52 0 R/XYZ 85.5 745.3 0]/Parent 133 0 R/Prev 149 0 R>> endobj 151 0 obj < /Dest[52 0 R/XYZ 128.2 409 0]/Parent 150 0 R/Next 152 0 R>> endobj 152 0 obj < /Dest[52 0 R/XYZ 128.2 232.7 0]/Parent 150 0 R/Prev 151 0 R>> endobj 153 0 obj < /Dest[55 0 R/XYZ 78.3 745.4 0]/Parent 127 0 R/Prev 133 0 R>> endobj 154 0 obj < /Dest[58 0 R/XYZ 85.5 745.3 0]/Parent 153 0 R/Next 155 0 R>> endobj 155 0 obj < /Dest[58 0 R/XYZ 85.5 414.8 0]/Parent 153 0 R/Prev 154 0 R/Next 156 0 R>> endobj 156 0 obj < /Dest[61 0 R/XYZ 85.5 745.3 0]/Parent 153 0 R/Prev 155 0 R/Next 160 0 R>> endobj 157 0 obj < /Dest[67 0 R/XYZ 92.7 745.3 0]/Parent 156 0 R/Next 158 0 R>> endobj 158 0 obj < /Dest[67 0 R/XYZ 92.7 530.6 0]/Parent 156 0 R/Prev 157 0 R/Next 159 0 R>> endobj 159 0 obj < /Dest[70 0 R/XYZ 92.7 745.4 0]/Parent 156 0 R/Prev 158 0 R>> endobj 160 0 obj < /Dest[73 0 R/XYZ 85.5 745.3 0]/Parent 153 0 R/Prev 156 0 R/Next 161 0 R>> endobj 161 0 obj < /Dest[76 0 R/XYZ 85.5 745.3 0]/Parent 153 0 R/Prev 160 0 R>> endobj 84 0 obj <> endobj 82 0 obj <> >> endobj 83 0 obj <> endobj 162 0 obj <> endobj 163 0 obj < /Creator /Producer /CreationDate(D:20100216025533+02'00')>> endobj xref 0 164 0000000000 65535 f 0000158943 00000 n 0000000019 00000 n 0000002671 00000 n 0000159088 00000 n 0000002692 00000 n 0000004556 00000 n 0000159233 00000 n 0000004577 00000 n 0000007988 00000 n 0000159378 00000 n 0000008009 00000 n 0000011468 00000 n 0000159525 00000 n 0000011490 00000 n 0000012472 00000 n 0000159672 00000 n 0000012493 00000 n 0000016195 00000 n 0000159837 00000 n 0000016217 00000 n 0000018473 00000 n 0000159984 00000 n 0000018495 00000 n 0000021230 00000 n 0000160131 00000 n 0000021252 00000 n 0000023519 00000 n 0000160278 00000 n 0000023541 00000 n 0000025692 00000 n 0000160425 00000 n 0000025714 00000 n 0000026254 00000 n 0000160572 00000 n 0000026275 00000 n 0000030433 00000 n 0000160719 00000 n 0000030455 00000 n 0000033322 00000 n 0000160866 00000 n 0000033344 00000 n 0000036894 00000 n 0000161013 00000 n 0000036916 00000 n 0000039867 00000 n 0000161160 00000 n 0000039889 00000 n 0000042030 00000 n 0000161307 00000 n 0000042052 00000 n 0000044812 00000 n 0000161454 00000 n 0000044834 00000 n 0000048819 00000 n 0000161601 00000 n 0000048841 00000 n 0000051690 00000 n 0000161748 00000 n 0000051712 00000 n 0000053829 00000 n 0000161895 00000 n 0000053851 00000 n 0000056298 00000 n 0000162042 00000 n 0000056320 00000 n 0000058141 00000 n 0000162189 00000 n 0000058163 00000 n 0000059838 00000 n 0000162354 00000 n 0000059860 00000 n 0000060828 00000 n 0000162501 00000 n 0000060849 00000 n 0000062111 00000 n 0000162648 00000 n 0000062133 00000 n 0000063383 00000 n 0000162795 00000 n 0000063405 00000 n 0000067015 00000 n 0000171114 00000 n 0000171275 00000 n 0000170832 00000 n 0000067037 00000 n 0000069104 00000 n 0000069126 00000 n 0000069320 00000 n 0000069611 00000 n 0000069775 00000 n 0000086449 00000 n 0000086472 00000 n 0000086674 00000 n 0000087266 00000 n 0000087709 00000 n 0000099597 00000 n 0000099620 00000 n 0000099827 00000 n 0000100290 00000 n 0000100611 00000 n 0000122251 00000 n 0000122275 00000 n 0000122472 00000 n 0000123139 00000 n 0000123652 00000 n 0000129999 00000 n 0000130022 00000 n 0000130222 00000 n 0000130600 00000 n 0000130842 00000 n 0000137387 00000 n 0000137410 00000 n 0000137615 00000 n 0000137959 00000 n 0000138175 00000 n 0000155227 00000 n 0000155251 00000 n 0000155446 00000 n 0000156103 00000 n 0000156604 00000 n 0000158093 00000 n 0000158116 00000 n 0000158309 00000 n 0000158608 00000 n 0000158776 00000 n 0000158886 00000 n 0000162942 00000 n 0000163002 00000 n 0000163295 00000 n 0000163444 00000 n 0000163650 00000 n 0000163957 00000 n 0000164135 00000 n 0000164351 00000 n 0000164541 00000 n 0000164788 00000 n 0000165023 00000 n 0000165258 00000 n 0000165517 00000 n 0000165740 00000 n 0000166014 00000 n 0000166337 00000 n 0000166608 00000 n 0000166810 00000 n 0000167041 00000 n 0000167273 00000 n 0000167532 00000 n 0000167750 00000 n 0000168025 00000 n 0000168348 00000 n 0000168534 00000 n 0000168723 00000 n 0000168922 00000 n 0000169148 00000 n 0000169374 00000 n 0000169613 00000 n 0000169864 00000 n 0000170018 00000 n 0000170181 00000 n 0000170419 00000 n 0000170634 00000 n 0000171392 00000 n 0000171509 00000 n trailer < <4CFD73664E14F97000964713C65ED1DF> ] /DocChecksum /C87BCC45932BCD123C69254390457CBB >> startxref 171759 %%EOF aprx-2.08.svn593/test.c0000644000175000017500000000053612005774517013614 0ustar colincolin#include static int aprspass(const char *mycall) { int a = 0, h = 29666, c; for (; *mycall; ++mycall) { c = 0xFF & *mycall; if (!(('0' <= c && c <= '9') || ('A' <= c && c <= 'Z'))) break; h ^= ((0xFF & *mycall) * (a ? 1 : 256)); a = !a; } return h; } main() { printf("APRSPASS: %d\n", aprspass("OH2MQK-1")); return 0; } aprx-2.08.svn593/TIMESTAMP-AT-APRSIS0000644000175000017500000002236312005774520015254 0ustar colincolin A PROPOSAL FOR TIMESTAMPS AT APRSIS It can be taken as granted that today at least server systems are running with NTP service keeping their system clocks within a few milliseconds of UTC time. Systems utilized as APRS IGATE should be able to utilize NTP service as well, and keep their internal time well disciplined. SimpleNTP clients are available even for embedded system code bases making all kinds of internet capable systems able to have high quality time management. Timestamps are added on APRS text lines as prefixes of 8 characters. Eighth character is always ':'. Details are in "ENCODING" part. TRANSPORT COMPATIBILITY As APRSIS clients would not know at first what to do with the timestamps, a transport compatibility must be introduced: * APRSIS server announces on its connection message that it is TIMESTAMP capable, and clients MUST NOT send timestamps to APRSIS server without that capability # telnet aprsisserver 14580 Connected to aprsisserver Escape character is '^]'. # javAPRSSrvr 4.0b1 [APRSIS TIMESTAMP] Capability announcements follow style of IMAPv4 protocol, where capability tokens are in square brackets, and are separated by space character. * If a client does not send timestamped data to APRSIS, APRSIS server adds the timestamp upon receiving the line. APRSIS server does this conditional timestamping also for packets received via UDP for initial deployment compatibility. * An APRSIS server connected to upstream with TCP is a client, and must strip received/internal timestamps from packets sent to upstream, if the upstream does not announce TIMESTAMP capability. If upstream does not send timestamps for any reason, APRSIS server adds it locally. * A client that has not announced timestamp awareness does not receive timestamps from APRSIS, and furthermore, APRSIS is filtering all packets with maximum age of 15 seconds. * A client that sends to APRSIS a text line with recognized timestamp will get also timestampped lines sent up to itself. No maximum age filtering is applied to clients that are timestamp capable, and therefore those clients must be able to do timestamp age analysis themselves. Special timestamp-line "AAAAAAA:\n" tells APRSIS server, that the client is APRSIS aware, and while it has nothing to actually send to APRSIS at this time, it does want all data sent to it to contain timestamps. APRSIS server software needs a configuration parameter udptimestamps = which defaults to "false". When the value is "false" the software will not send timestampped data to core peer machines over UDP. There probably is no need for an Adjunct Filter that knows about timestamps. INITIAL DEPLOYMENT OF TIMESTAMP CAPABILITY Because APRSIS core uses UDP protocol, and thus can not be aware of remote node capabilities, the deployment of timestamp aware APRSIS servers at APRSIS core has two phases: 1) Software has configuration parameter "udptimestamps" non defined or defined as "false". 2) After all core APRSIS systems are running timestamp aware software, the configuration parameter can be changed to "udptimestamps = true" and APRSIS server starts to send timestamped packets over UDP to all of its peers. --> peer machines do not need to locally add timestamps at reception time. Everybody else can take TIMESTAMP capable system into use at any time that is convenient to them, unless communication involves UDP. ENCODING Use NTP timestamp (see RFC 2030) based time abstraction: Since NTP timestamps are cherished data and, in fact, represent the main product of the protocol, a special timestamp format has been established. NTP timestamps are represented as a 64-bit unsigned fixed-point number, in seconds relative to 0h on 1 January 1900. The integer part is in the first 32 bits and the fraction part in the last 32 bits. In the fraction part, the non-significant low order can be set to 0. Take highest 32+10 bits of the timestamp, and encode those in BASE64 characters, most significant character first. This produces 7 encoded characters with time resolution of 1/1024 seconds. With 6 encoded characters the time resolution would be 1/16, which is felt to be too coarse. A hex encoded byte sequence of NTP timestamp looks like this: cf b4 ff a4 b5 a8 8b f9 it represent timestamp 3484745636.709603071 (2010/06/05 19:53:56) and encoded timestamp value is: z7T/pLW The APRSIS passes around APRS messages as lines of text: OH2JCQ>APX195,TCPIP*,qAC,T2FINLAND:=6013.63N/02445.59E-Jani Adding 7 character encoded timestamp + ":" character in the beginning of a line would make this: z7T/pLW:OH2JCQ>APX195,TCPIP*,qAC,T2FINLAND:=6013.63N/02445.59E-Jani There 8th character is always ':', and preceding 7 bytes present _current_ timestamp at 1/1024 second resolution. Timestamp value "AAAAAAA:" is "no timestamp" (decodes as time 0.0) and a client can use it to tell that it has no idea of timestamp, but it wants to receive timestamped data. An APRSIS data collector can enable timestamp reception by sending line: "AAAAAAA:\n". Packets with timestamps outside -1 to +N seconds limits are sign that communication is retrying too much, or otherwise delayed, and such packets are to be discarded. (Services like APRSFI, FINDU et.al. may want even older packets!) To be compatible with NTP Era roll-over, the timestamp must really be treated as 64-bit unsigned long integer, and compared with twos complement arithmetic using overflows. TIMESTAMP ENCODING CODE EXAMPLE Example UNIX system C code to produce and encode a timestamp: // Time Base Conversion Macros // // The NTP timebase is 00:00 Jan 1 1900. The local // time base is 00:00 Jan 1 1970. Convert between // these two by added or substracting 70 years // worth of time. Note that 17 of these years were // leap years. #define TIME_BASEDIFF (((70U*365U + 17U) * 24U*3600U)) #define TIME_LOCAL_TO_NTP(t) ((t)+TIME_BASEDIFF) void unix_tv_to_ntp(struct timeval *tv, uint64_t *ntp) { // Reciprocal conversion of tv_usec to fractional NTP seconds // Multiply tv_usec by ((2^64)/1_000_000) / (2^32) uint64_t fract = 18446744073709ULL * (uint32_t)(tv->tv_usec); // Scale it back by 32 bit positions fract >>= 32; // Place seconds on upper 32 bits fract += ((uint64_t)TIME_LOCAL_TO_NTP(tv->tv_sec)) << 32; // Deliver time to caller *ntp = fract; } static const char *BASE64EncodingDictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" "+/"; void encode_aprsis_ntptimestamp(uint64_t ntptime, char timestamp[8]) { int i; ntptime >>= 22; // scale to 1/1024 seconds for (i = 6; i >= 0; --i) { int n = (((int)ntptime) & 0x3F); // lowest 6 bits // printf(" [n=%d]\n", n); ntptime >>= 6; timestamp[i] = BASE64EncodingDictionary[n]; } timestamp[7] = 0; } static const int8_t BASE64DecodingDictionary[128] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ' ', '!', '"', '#' -1, -1, -1, -1, // '$', '%', '&'', '\'' -1, -1, -1, 62, // '(', ')', '*', '+', -1, -1, -1, 63, // ',', '-', '.', '/' 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0' .. '9' -1, -1, -1, -1, -1, -1, // ':', ';', '<', '=', '>', '?' -1, 0, 1, 2, 3, 4, 5, 6, // '@', 'A' .. 'G' 7, 8, 9, 10, 11, 12, 13, 14, // 'H' .. 'O' 15, 16, 17, 18, 19, 20, 21, 22, // 'P' .. 'W' 23, 24, 25, -1, -1, -1, -1, -1, // 'X'..'Z', '[', '\\', ']', '^', '_' -1, 26, 27, 28, 29, 30, 31, 32, // '`', 'a' .. 'g' 33, 34, 35, 36, 37, 38, 39, 40, // 'h' .. 'o' 41, 42, 43, 44, 45, 46, 47, 48, // 'p' .. 'w' 49, 50, 51, -1, -1, -1, -1, -1 }; // 'x'..'z', ... int decode_aprsis_ntptimestamp(char timestamp[8], uint64_t *ntptimep) { uint64_t ntptime = 0; int i, n; char c; for (i = 0; i < 7; ++i) { c = timestamp[i]; if (c <= 0 || c > 127) return -1; // BARF! n = BASE64DecodingDictionary[(int)c]; // printf(" [n=%d]\n", n); if (n < 0) { // Should not happen! return -1; // Decode fail! } ntptime <<= 6; ntptime |= n; } ntptime <<= 22; *ntptimep = ntptime; return 0; // Decode OK } You may choose to produce timestamps with coarser resolution than 1/1024 seconds. Let least significant bits to be zero. The about 1 millisecond resolution is a compromise in between 1 second, and sub-nanosecond resolutions. A heavy producer and consumer of timestamps, like an APRSIS server, may have single thread waking up 10 times per second and producing a new timestamp object every time, and overwriting global storage pointer to "current" one. For access there is no need to do any sort of synchronizations. Then received messages just copy that timestamp if necessary, and not run its production by themselves. Note: There is no need to convert NTP timestamps to local time in this application. It is quite enough that system receiving NTP timestamps does regularly create current reference NTP timestamp to be able to determine offset from received timestamp to reference time. aprx-2.08.svn593/digipeater.c0000644000175000017500000022070612314016324014741 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" static int digi_count; static struct digipeater **digis; #define TOKENBUCKET_INTERVAL 5 // 5 seconds per refill. // 60/5 part of "ratelimit" to be max // that token bucket can be filled to. static struct timeval tokenbucket_timer; struct viastate { int hopsreq; int hopsdone; int tracereq; int tracedone; int digireq; int digidone; int fixthis; int fixall; int probably_heard_direct; }; struct digistate { struct viastate v; int ax25addrlen; uint8_t ax25addr[90]; // 70 for address, a bit more for "body" }; #define AX25ADDRMAXLEN 70 // SRC + DEST + 8*VIA (7 bytes each) #define AX25ADDRLEN 7 #define AX25HBIT 0x80 #define AX25ATERM 0x01 static char * tracewords[] = { "WIDE","TRACE","RELAY" }; static int tracewordlens[] = { 4, 5, 5 }; static const struct tracewide default_trace_param = { 4, 4, 1, 3, tracewords, tracewordlens }; static char * widewords[] = { "WIDE","RELAY" }; static int widewordlens[] = { 4,5 }; static const struct tracewide default_wide_param = { 4, 4, 0, 2, widewords, widewordlens }; static int run_tokenbucket_timers(void); float ratelimitmax = 9999999.9; float rateincrementmax = 9999999.9; /* * regex_filter_add() -- adds configured regular expressions * into forbidden patterns list. * * These are actually processed on TNC2 format text line, and not * AX.25 datastream per se. */ static int regex_filter_add(struct configfile *cf, struct digipeater_source *src, char *param1, char *str) { int rc; int groupcode = -1; regex_t re, *rep; char errbuf[2000]; if (strcmp(param1, "source") == 0) { groupcode = 0; } else if (strcmp(param1, "destination") == 0) { groupcode = 1; } else if (strcmp(param1, "via") == 0) { groupcode = 2; } else if (strcmp(param1, "data") == 0) { groupcode = 3; } else { printf("%s:%d ERROR: Bad RE target: '%s' must be one of: source, destination, via\n", cf->name, cf->linenum, param1); return 1; } if (!*str) { printf("%s:%d ERROR: Expected RE pattern missing or a NUL string.\n", cf->name, cf->linenum); return 1; /* Bad input.. */ } param1 = str; str = config_SKIPTEXT(str, NULL); // Handle quoted string str = config_SKIPSPACE(str); memset(&re, 0, sizeof(re)); rc = regcomp(&re, param1, REG_EXTENDED | REG_NOSUB); if (rc != 0) { /* Something is bad.. */ *errbuf = 0; regerror(rc, &re, errbuf, sizeof(errbuf)); printf("%s:%d ERROR: Bad POSIX RE input, error: %s\n", cf->name, cf->linenum, errbuf); return 1; } /* param1 and str were processed successfully ... */ rep = calloc(1,sizeof(*rep)); *rep = re; switch (groupcode) { case 0: src->sourceregscount += 1; src->sourceregs = realloc(src->sourceregs, src->sourceregscount * sizeof(void *)); src->sourceregs[src->sourceregscount - 1] = rep; break; case 1: src->destinationregscount += 1; src->destinationregs = realloc(src->destinationregs, src->destinationregscount * sizeof(void *)); src->destinationregs[src->destinationregscount - 1] = rep; break; case 2: src->viaregscount += 1; src->viaregs = realloc(src->viaregs, src->viaregscount * sizeof(void *)); src->viaregs[src->viaregscount - 1] = rep; break; case 3: src->dataregscount += 1; src->dataregs = realloc(src->dataregs, src->dataregscount * sizeof(void *)); src->dataregs[src->dataregscount - 1] = rep; break; } return 0; // OK state } static int match_tracewide(const char *via, const struct tracewide *twp) { int i; if (twp == NULL) return 0; for (i = 0; i < twp->nkeys; ++i) { // if (debug>2) printf(" match:'%s'",twp->keys[i]); if (memcmp(via, twp->keys[i], twp->keylens[i]) == 0) { return twp->keylens[i]; } } return 0; } static int match_aliases(const char *via, struct aprx_interface *txif) { int i; for (i = 0; i < txif->aliascount; ++i) { if (strcmp(via, txif->aliases[i]) == 0) return 1; } return 0; } static int count_single_tnc2_tracewide(struct viastate *state, const char *viafield, const int istrace, const int matchlen, const int viaindex) { const char *p = viafield + matchlen; const char reqc = p[0]; const char c = p[1]; const char remc = p[2]; int req, done; int hasHflag = (strchr(viafield,'*') != NULL); // Non-matched case, may have H-bit flag if (matchlen == 0) { /* state->hopsreq += 0; state->hopsdone += 0; state->tracereq += 0; state->tracedone += 0; */ state->digireq += 1; state->digidone += hasHflag; if (viaindex == 2 && !hasHflag) state->probably_heard_direct = 1; // if (debug>1) printf(" a[req=%d,done=%d,trace=%d]",0,0,hasHflag); return 0; } // Is the character following matched part one of: [1-7] if (!('1' <= reqc && reqc <= '7')) { // Not a digit, this is single matcher.. state->hopsreq += 1; state->hopsdone += hasHflag; if (istrace) { state->tracereq += 1; state->tracedone += hasHflag; } if (viaindex == 2 && !hasHflag) state->probably_heard_direct = 1; // if (debug>1) printf(" d[req=%d,done=%d]",1,hasHflag); return 0; } req = reqc - '0'; if (c == '*' && remc == 0) { // WIDE1* state->hopsreq += req; state->hopsdone += req; if (istrace) { state->tracereq += req; state->tracedone += req; } // if (debug>1) printf(" e[req=%d,done=%d]",req,req); return 0; } if (c == 0) { // Bogus WIDE1 - uidigi puts these out. state->fixthis = 1; state->hopsreq += req; state->hopsdone += req; if (istrace) { state->tracereq += req; state->tracedone += req; } // if (debug>1) printf(" E[req=%d,done=%d]",req,req); return 0; } // Not WIDE1- if (c != '-') { state->hopsreq += 1; state->hopsdone += hasHflag; if (istrace) { state->tracereq += 1; state->tracedone += hasHflag; } // if (debug>1) printf(" f[req=%d,done=%d]",1,hasHflag); return 0; } // OK, it is "WIDEn-" plus "N" if ('0' <= remc && remc <= '7' && p[3] == 0) { state->hopsreq += req; done = req - (remc - '0'); state->hopsdone += done; if (done < 0) { // Something like "WIDE3-7", which is definitely bogus! state->fixall = 1; if (viaindex == 2 && !hasHflag) state->probably_heard_direct = 1; return 0; } if (istrace) { state->tracereq += req; state->tracedone += done; } if (viaindex == 2) { if (memcmp("TRACE",viafield,5)==0) // A real "TRACE" in first slot? state->probably_heard_direct = 1; else if (!hasHflag && done == 0) // WIDE3-3 on first slot state->probably_heard_direct = 1; } // if (debug>1) printf(" g[req=%d,done=%d%s]",req,done,hasHflag ? ",Hflag!":""); return 0; } else if (('8' <= remc && remc <= '9' && p[3] == 0) || (remc == '1' && '0' <= p[3] && p[3] <= '5' && p[4] == 0)) { // The request has SSID value in range of 8 to 15 state->fixall = 1; if (viaindex == 2 && !hasHflag) state->probably_heard_direct = 1; return 0; } else { // Yuck, impossible/syntactically invalid state->hopsreq += 1; state->hopsdone += hasHflag; if (istrace) { state->tracereq += 1; state->tracedone += hasHflag; } if (viaindex == 2 && !hasHflag) state->probably_heard_direct = 1; // if (debug>1) printf(" h[req=%d,done=%d]",1,hasHflag); return 1; } } static int match_transmitter(const char *viafield, const struct digipeater_source *src, const int lastviachar) { struct aprx_interface *aif = src->parent->transmitter; int tlen = strlen(aif->callsign); if (memcmp(viafield, aif->callsign, tlen) == 0) { if (viafield[tlen] == lastviachar) return 1; } return 0; } static int try_reject_filters(const int fieldtype, const char *field, struct digipeater_source *src) { int i; int stat = 0; switch (fieldtype) { case 0: // Source for (i = 0; i < src->sourceregscount; ++i) { stat = regexec(src->sourceregs[i], field, 0, NULL, 0); if (stat == 0) return 1; /* MATCH! */ } if (memcmp("MYCALL",field,6)==0) return 1; if (memcmp("N0CALL",field,6)==0) return 1; if (memcmp("NOCALL",field,6)==0) return 1; break; case 1: // Destination for (i = 0; i < src->destinationregscount; ++i) { int stat = regexec(src->destinationregs[i], field, 0, NULL, 0); if (stat == 0) return 1; /* MATCH! */ } if (memcmp("MYCALL",field,6)==0) return 1; if (memcmp("N0CALL",field,6)==0) return 1; if (memcmp("NOCALL",field,6)==0) return 1; break; case 2: // Via for (i = 0; i < src->viaregscount; ++i) { int stat = regexec(src->viaregs[i], field, 0, NULL, 0); if (stat == 0) return 1; /* MATCH! */ } if (memcmp("MYCALL",field,6)==0) return 1; if (memcmp("N0CALL",field,6)==0) return 1; if (memcmp("NOCALL",field,6)==0) return 1; break; case 3: // Data for (i = 0; i < src->dataregscount; ++i) { int stat = regexec(src->dataregs[i], field, 0, NULL, 0); if (stat == 0) return 1; /* MATCH! */ } break; default: if (debug) printf("try_reject_filters(fieldtype=%d) - CODE BUG\n", fieldtype); return 1; } if (stat != 0 && stat != REG_NOMATCH) { // Some odd reason for an error? } return 0; } /* Parse executed and requested WIDEn-N/TRACEn-N info */ static int parse_tnc2_hops(struct digistate *state, struct digipeater_source *src, struct pbuf_t *pb) { const char *p = pb->dstcall_end+1; const char *s; const struct digipeater *digi = src->parent; const char *lastviastar; char viafield[15]; // temp buffer for many uses int have_fault = 0; int viaindex = 1; // First via index will be 2.. int activeviacount = 0; int len; int digiok; if (debug>1) printf(" hops count of buffer: %s\n",p); if (src->src_relaytype == DIGIRELAY_THIRDPARTY) { state->v.hopsreq = 1; // Bonus for tx-igated 3rd-party frames state->v.tracereq = 1; // Bonus for tx-igated 3rd-party frames state->v.hopsdone = 0; state->v.tracedone = 0; state->v.probably_heard_direct = 1; return 0; } // Copy the SRCCALL part of SRCALL>DSTCALL to viafield[] buffer len = pb->srccall_end - pb->data; if (len >= sizeof(viafield)) len = sizeof(viafield)-1; memcpy(viafield, pb->data, len); viafield[len] = 0; // if (debug>2)printf(" srccall='%s'",viafield); if (try_reject_filters(0, viafield, src)) { if (debug>1) printf(" - Src filters reject\n"); return 1; // Src reject filters } // Copy the DSTCALL part of SRCALL>DSTCALL to viafield[] buffer len = pb->dstcall_end - pb->srccall_end -1; if (len >= sizeof(viafield)) len = sizeof(viafield)-1; memcpy(viafield, pb->srccall_end+1, len); viafield[len] = 0; // if (debug>2)printf(" destcall='%s'",viafield); if (try_reject_filters(1, viafield, src)) { if (debug>1) printf(" - Dest filters reject\n"); return 1; // Dest reject filters } // Where is the last via-field with a start on it? len = pb->info_start - p; if (len < 0) len=0; lastviastar = memrchr(p, len, '*'); // Loop over VIA fields to see if we need to digipeat anything. while (p < pb->info_start && !have_fault) { len = 0; if (*p == ':') { // A round may stop at ':' found at the end of the processed field, // then next round finds it at the start of the field. break; } // Scan for a VIA field ... for (s = p; s < pb->info_start; ++s) { if (*s == ',' || *s == ':') { // ... until comma or double-colon. break; } } // [p..s) is now one VIA field. if (s == p && *p != ':') { // BAD! have_fault = 1; if (debug>1) printf(" S==P "); break; } if (*p == 'q') break; // APRSIS q-constructs.. ++viaindex; // Pick-up a viafield to separate buffer for processing len = s-p; if (len >= sizeof(viafield)) len = sizeof(viafield)-2; memcpy(viafield, p, len); viafield[len] = 0; if (*s == ',') ++s; p = s; // Only last via field with H-bit is indicated at TNC2 format, // but this digi code logic needs it at every VIA field where // it is set. Therefore this crooked way to add it to picked // up fields. if (strchr(viafield,'*') == NULL) { // If it exists somewhere, and we are not yet at it.. if (lastviastar != NULL && p < lastviastar) strcat(viafield,"*"); // we do know that there is space for this. } if (debug>1) printf(" - ViaField[%d]: '%s'\n", viaindex, viafield); // VIA-field picked up, now analyze it.. if (try_reject_filters(2, viafield, src)) { if (debug>1) printf(" - Via filters reject\n"); return 1; // via reject filters } // Transmitter callsign match with H-flag set. if (match_transmitter(viafield, src, '*')) { if (debug>1) printf(" - Tx match reject\n"); // Oops, LOOP! I have transmit this in past // (according to my transmitter callsign present // in a VIA field!) return 1; } // If there is no '*' meaning this has not been // processed, then this is active field.. if (strchr(viafield, '*') == NULL) ++activeviacount; digiok = 0; // If first active field (without '*') matches // transmitter or alias, then this digi is accepted // regardless if it is APRS or some other protocol. if (activeviacount == 1 && (match_transmitter(viafield, src, 0) || match_aliases(viafield, digi->transmitter))) { if (debug>1) printf(" - Tx match accept!\n"); state->v.hopsreq += 1; state->v.tracereq += 1; digiok = 1; } // .. otherwise following rules are applied only to APRS packets. if (pb->is_aprs) { if ((len = match_tracewide(viafield, src->src_trace))) { have_fault = count_single_tnc2_tracewide(&state->v, viafield, 1, len, viaindex); if (!have_fault) digiok = 1; } else if ((len = match_tracewide(viafield, digi->trace))) { have_fault = count_single_tnc2_tracewide(&state->v, viafield, 1, len, viaindex); if (!have_fault) digiok = 1; } else if ((len = match_tracewide(viafield, src->src_wide))) { have_fault = count_single_tnc2_tracewide(&state->v, viafield, 0, len, viaindex); if (!have_fault) digiok = 1; } else if ((len = match_tracewide(viafield, digi->wide))) { have_fault = count_single_tnc2_tracewide(&state->v, viafield, 0, len, viaindex); if (!have_fault) digiok = 1; } else { // No match on trace or wide, but if there was earlier // match on interface or alias, then it set "digiok" for us. } } if (state->v.fixthis || state->v.fixall) { // Argh.. bogus WIDEn seen, which is what UIDIGIs put out.. // Also some other broken requests are "fixed": like WIDE3-7 // Fixing it: We set the missing H-bit, and continue processing. // (That fixing is done in incoming AX25 address field, which // we generally do not touch - with this exception.) pb->ax25addr[ AX25ADDRLEN*viaindex + AX25ADDRLEN-1 ] |= AX25HBIT; state->v.fixthis = 0; } if (!digiok) { if (debug>1) printf(" this via field is not matching with a TRACE, WIDE, ALIAS or INTERFACE.\n"); break; } } if (debug>1) printf(" req=%d,done=%d [%s,%s,%s]\n", state->v.hopsreq,state->v.hopsdone, have_fault ? "FAULT":"OK", (state->v.hopsreq > state->v.hopsdone) ? "DIGIPEAT":"DROP", (state->v.tracereq > state->v.tracedone) ? "TRACE":"WIDE"); return have_fault; } static void free_tracewide(struct tracewide *twp) { int i; if (twp == NULL) return; if (twp->keys) { for (i = 0; i < twp->nkeys; ++i) free((void*)(twp->keys[i])); free(twp->keys); } if (twp->keylens) free((void*)(twp->keylens)); free(twp); } static void free_source(struct digipeater_source *src) { if (src == NULL) return; free(src); } static struct tracewide *digipeater_config_tracewide(struct configfile *cf, int is_trace) { char *name, *param1; char *str = cf->buf; int has_fault = 0; int nkeys = 0; char **keywords = NULL; int *keylens = NULL; int maxreq = 4; int maxdone = 4; struct tracewide *tw; while (readconfigline(cf) != NULL) { if (configline_is_comment(cf)) continue; /* Comment line, or empty line */ // It can be severely indented... str = config_SKIPSPACE(cf->buf); name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRLOWER(name); param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (is_trace) { if (strcmp(name, "") == 0) { break; } } else { if (strcmp(name, "") == 0) { break; } } // ... actual parameters if (strcmp(name,"maxreq") == 0) { maxreq = atoi(param1); // if (debug) printf(" maxreq %d\n",maxreq); } else if (strcmp(name,"maxdone") == 0) { maxdone = atoi(param1); // if (debug) printf(" maxdone %d\n",maxdone); } else if (strcmp(name,"keys") == 0) { char *k = strtok(param1, ","); for (; k ; k = strtok(NULL,",")) { ++nkeys; // if (debug) printf(" n=%d key='%s'\n",nkeys,k); keywords = realloc(keywords, sizeof(char*) * nkeys); keywords[nkeys-1] = strdup(k); keylens = realloc(keylens, sizeof(int) * nkeys); keylens[nkeys-1] = strlen(k); } } else { has_fault = 1; printf("%s:%d ERROR: Unknown keyword inside %s subblock: '%s'\n", cf->name, cf->linenum, is_trace ? "":"", name); } } if (has_fault) { int i; for (i = 0; i < nkeys; ++i) free(keywords[i]); if (keywords != NULL) free(keywords); if (keylens != NULL) free(keylens); return NULL; } tw = calloc(1,sizeof(*tw)); tw->maxreq = maxreq; tw->maxdone = maxdone; tw->is_trace = is_trace; tw->nkeys = nkeys; tw->keys = keywords; tw->keylens = keylens; return tw; } static struct digipeater_source *digipeater_config_source(struct configfile *cf) { char *name, *param1; char *str = cf->buf; int has_fault = 0; int viscous_delay = 0; float ratelimit = 120; float rateincrement = 60; struct aprx_interface *source_aif = NULL; struct digipeater_source *source = NULL; digi_relaytype relaytype = DIGIRELAY_DIGIPEAT; struct filter_t *filters = NULL; struct tracewide *source_trace = NULL; struct tracewide *source_wide = NULL; struct digipeater_source regexsrc; #ifndef DISABLE_IGATE char *via_path = NULL; char *msg_path = NULL; uint8_t ax25viapath[AX25ADDRLEN]; uint8_t msgviapath[AX25ADDRLEN]; #endif memset(®exsrc, 0, sizeof(regexsrc)); #ifndef DISABLE_IGATE memset(ax25viapath, 0, sizeof(ax25viapath)); memset(msgviapath, 0, sizeof(msgviapath)); #endif while (readconfigline(cf) != NULL) { if (configline_is_comment(cf)) continue; /* Comment line, or empty line */ // It can be severely indented... str = config_SKIPSPACE(cf->buf); name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRLOWER(name); param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (strcmp(name, "") == 0) { break; // ... actual parameters } else if (strcmp(name,"source") == 0) { if (debug) printf("%s:%d source = '%s'\n", cf->name, cf->linenum, param1); if (strcasecmp(param1,"$mycall") == 0) param1 = (char*)mycall; source_aif = find_interface_by_callsign(param1); if (source_aif == NULL) { has_fault = 1; printf("%s:%d ERROR: Digipeater source '%s' not found\n", cf->name, cf->linenum, param1); } if (debug>1) printf(" .. source_aif = %p\n", source_aif); } else if (strcmp(name, "viscous-delay") == 0) { viscous_delay = atoi(param1); if (debug) printf(" viscous-delay = %d\n",viscous_delay); if (viscous_delay < 0) { printf("%s:%d ERROR: Bad value for viscous-delay: '%s'\n", cf->name, cf->linenum, param1); viscous_delay = 0; has_fault = 1; } if (viscous_delay > 9) { printf("%s:%d ERROR: Too large value for viscous-delay: '%s'\n", cf->name, cf->linenum, param1); viscous_delay = 9; has_fault = 1; } } else if (strcmp(name, "ratelimit") == 0) { char *param2 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); rateincrement = (float)atof(param1); ratelimit = (float)atof(param2); if (rateincrement < 0.01 || rateincrement > rateincrementmax) rateincrement = 60; if (ratelimit < 0.01 || ratelimit > ratelimitmax) ratelimit = 120; if (ratelimit < rateincrement) rateincrement = ratelimit; if (debug) printf(" .. ratelimit %f %f\n", rateincrement, ratelimit); } else if (strcmp(name,"regex-filter") == 0) { if (regex_filter_add(cf, ®exsrc, param1, str)) { // prints errors internally has_fault = 1; } #ifndef DISABLE_IGATE } else if (strcmp(name, "via-path") == 0) { // Validate that source callsign is "APRSIS" // or "DPRS" for this parameter if (source_aif == NULL || (strcmp(source_aif->callsign,"APRSIS") != 0 && strcmp(source_aif->callsign,"DPRS") != 0)) { printf("%s:%d ERROR: via-path parameter is available only on 'source APRSIS' and 'source DPRS' cases.\n", cf->name, cf->linenum); has_fault = 1; continue; } via_path = strdup(param1); config_STRUPPER(via_path); if (parse_ax25addr(ax25viapath, via_path, 0x00)) { has_fault = 1; printf("%s:%d ERROR: via-path parameter is not valid AX.25 callsign: '%s'\n", cf->name, cf->linenum, via_path); free(via_path); via_path = NULL; continue; } if (debug) printf("via-path '%s'\n", via_path); } else if (strcmp(name, "msg-path") == 0) { // Validate that source callsign is "APRSIS" // or "DPRS" for this parameter if (source_aif == NULL || (strcmp(source_aif->callsign,"APRSIS") != 0 && strcmp(source_aif->callsign,"DPRS") != 0)) { printf("%s:%d ERROR: msg-path parameter is available only on 'source APRSIS' and 'source DPRS' cases.\n", cf->name, cf->linenum); has_fault = 1; continue; } msg_path = strdup(param1); config_STRUPPER(msg_path); if (parse_ax25addr(msgviapath, msg_path, 0x00)) { has_fault = 1; printf("%s:%d ERROR: msg-path parameter is not valid AX.25 callsign: '%s'\n", cf->name, cf->linenum, msg_path); free(msg_path); msg_path = NULL; continue; } if (debug) printf("msg-path '%s'\n", msg_path); #endif } else if (strcmp(name,"") == 0) { if (source_trace == NULL) { source_trace = digipeater_config_tracewide(cf, 1); // prints errors internally } else { has_fault = 1; printf("%s:%d ERROR: double definition of block.\n", cf->name, cf->linenum); } } else if (strcmp(name,"") == 0) { if (source_wide == NULL) { source_wide = digipeater_config_tracewide(cf, 0); // prints errors internally } else { has_fault = 1; printf("%s:%d ERROR: double definition of block.\n", cf->name, cf->linenum); } } else if (strcmp(name,"filter") == 0) { if (filter_parse(&filters, param1)) { // prints errors internally has_fault = 1; printf("%s:%d ERROR: Error at filter parser.\n", cf->name, cf->linenum); } else { if (debug) printf(" .. OK filter %s\n", param1); } } else if (strcmp(name,"relay-type") == 0 || // documented name strcmp(name,"relay-format") == 0 || // an alias strcmp(name,"digi-mode") == 0) { // very old alias config_STRLOWER(param1); if (strcmp(param1,"digipeat") == 0) { relaytype = DIGIRELAY_DIGIPEAT; } else if (strcmp(param1,"digipeated") == 0) { relaytype = DIGIRELAY_DIGIPEAT; } else if (strcmp(param1,"digipeater") == 0) { relaytype = DIGIRELAY_DIGIPEAT; } else if (strcmp(param1,"directonly") == 0) { relaytype = DIGIRELAY_DIGIPEAT_DIRECTONLY; } else if (strcmp(param1,"third-party") == 0) { relaytype = DIGIRELAY_THIRDPARTY; } else if (strcmp(param1,"3rd-party") == 0) { relaytype = DIGIRELAY_THIRDPARTY; } else { printf("%s:%d ERROR: Digipeater 's %s did not recognize: '%s' \n", cf->name, cf->linenum, name, param1); has_fault = 1; } } else { printf("%s:%d ERROR: Digipeater 's %s did not recognize: '%s' \n", cf->name, cf->linenum, name, param1); has_fault = 1; } } if (source_aif == NULL) { has_fault = 1; printf("%s:%d ERROR: Missing or bad 'source =' definition at this group.\n", cf->name, cf->linenum); } if (!has_fault && (source_aif != NULL)) { source = calloc(1,sizeof(*source)); source->src_if = source_aif; source->src_relaytype = relaytype; source->src_filters = filters; source->src_trace = source_trace; source->src_wide = source_wide; #ifndef DISABLE_IGATE source->via_path = via_path; source->msg_path = msg_path; memcpy(source->ax25viapath, ax25viapath, sizeof(ax25viapath)); memcpy(source->msgviapath, msgviapath, sizeof(msgviapath)); if (msg_path == NULL) { // default value of via-path ! source->msg_path = via_path; memcpy(source->msgviapath, ax25viapath, sizeof(ax25viapath)); } #endif source->viscous_delay = viscous_delay; source->tbf_limit = (ratelimit * TOKENBUCKET_INTERVAL)/60; source->tbf_increment = (rateincrement * TOKENBUCKET_INTERVAL)/60; source->tokenbucket = source->tbf_limit; // RE pattern reject filters source->sourceregscount = regexsrc.sourceregscount; source->sourceregs = regexsrc.sourceregs; source->destinationregscount = regexsrc.destinationregscount; source->destinationregs = regexsrc.destinationregs; source->viaregscount = regexsrc.viaregscount; source->viaregs = regexsrc.viaregs; source->dataregscount = regexsrc.dataregscount; source->dataregs = regexsrc.dataregs; } else { // Errors detected free_tracewide(source_trace); free_tracewide(source_wide); // filters_free(filters); // free regexsrc's allocations if (debug) printf("Seen errors at definition.\n"); } if (debug>1)printf(" .. definition returning %p\n",source); return source; } int digipeater_config(struct configfile *cf) { char *name, *param1; char *str = cf->buf; int has_fault = 0; int i; const int line0 = cf->linenum; struct aprx_interface *aif = NULL; float ratelimit = 60; float rateincrement = 60; float srcratelimit = 60; float srcrateincrement = 60; int sourcecount = 0; int dupestoretime = 30; // FIXME: parametrize! 30 is minimum.. struct digipeater_source **sources = NULL; struct digipeater *digi = NULL; struct tracewide *traceparam = NULL; struct tracewide *wideparam = NULL; while (readconfigline(cf) != NULL) { if (configline_is_comment(cf)) continue; /* Comment line, or empty line */ // It can be severely indented... str = config_SKIPSPACE(cf->buf); name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRLOWER(name); param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (strcmp(name, "") == 0) { break; } if (strcmp(name, "transmit") == 0 || strcmp(name, "transmitter") == 0) { if (strcasecmp(param1,"$mycall") == 0) param1 = (char*)mycall; aif = find_interface_by_callsign(param1); if (aif != NULL && (!aif->tx_ok)) { aif = NULL; // Not printf("%s:%d ERROR: This transmit interface has no TX-OK TRUE setting: '%s'\n", cf->name, cf->linenum, param1); has_fault = 1; } else if (aif != NULL && aif->txrefcount > 0) { aif = NULL; printf("%s:%d ERROR: This transmit interface is being used on multiple s as transmitter: '%s'\n", cf->name, cf->linenum, param1); has_fault = 1; } else if (aif == NULL) { printf("%s:%d ERROR: Unknown interface: '%s'\n", cf->name, cf->linenum, param1); has_fault = 1; } } else if (strcmp(name, "ratelimit") == 0) { char *param2 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); rateincrement = (float)atof(param1); ratelimit = (float)atof(param2); if (rateincrement < 0.01 || rateincrement > rateincrementmax) rateincrement = 60; if (ratelimit < 0.01 || ratelimit > ratelimitmax) ratelimit = 60; if (ratelimit < rateincrement) rateincrement = ratelimit; if (debug) printf(" .. ratelimit %f %f\n", rateincrement, ratelimit); } else if (strcmp(name, "srcratelimit") == 0) { char *param2 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); srcrateincrement = (float)atof(param1); srcratelimit = (float)atof(param2); if (srcrateincrement < 0.01 || srcrateincrement > rateincrementmax) srcrateincrement = 60; if (srcratelimit < 0.01 || srcratelimit > ratelimitmax) srcratelimit = 60; if (srcratelimit < srcrateincrement) srcrateincrement = srcratelimit; if (debug) printf(" .. srcratelimit %f %f\n", srcrateincrement, srcratelimit); } else if (strcmp(name, "") == 0) { if (traceparam == NULL) { traceparam = digipeater_config_tracewide(cf, 1); if (traceparam == NULL) { printf("%s:%d ERROR: definition failed!\n", cf->name, cf->linenum); has_fault = 1; } } else { printf("%s:%d ERROR: Double definition of !\n", cf->name, cf->linenum); has_fault = 1; } } else if (strcmp(name, "") == 0) { if (wideparam == NULL) { wideparam = digipeater_config_tracewide(cf, 0); if (wideparam == NULL) { printf("%s:%d ERROR: definition failed!\n", cf->name, cf->linenum); has_fault = 1; } } else { printf("%s:%d ERROR: Double definition of !\n", cf->name, cf->linenum); has_fault = 1; } } else if (strcmp(name, "") == 0) { struct digipeater_source *src = digipeater_config_source(cf); if (src != NULL) { // Found a source, link it! sources = realloc(sources, sizeof(void*) * (sourcecount+1)); sources[sourcecount] = src; ++sourcecount; } else { has_fault = 1; printf("%s:%d ERROR: definition failed\n", cf->name, cf->linenum); } } else { printf("%s:%d ERROR: Unknown config keyword: '%s'\n", cf->name, cf->linenum, name); has_fault = 1; continue; } } if (aif == NULL && !has_fault) { printf("%s:%d ERROR: Digipeater defined without transmit interface.\n", cf->name, cf->linenum); has_fault = 1; } if (sourcecount == 0 && !has_fault) { printf("%s:%d ERROR: Digipeater defined without :s.\n", cf->name, cf->linenum); has_fault = 1; } // Check that source definitions are unique for ( i = 0; i < sourcecount; ++i ) { int j; for (j = i+1; j < sourcecount; ++j) { if (sources[i]->src_if == sources[j]->src_if) { has_fault = 1; printf("%s:%d Two s on this definition use same : '%s'\n", cf->name, line0, sources[i]->src_if->callsign); } } } if (has_fault) { // Free allocated resources and link pointers, if any for ( i = 0; i < sourcecount; ++i ) { free_source(sources[i]); } if (sources != NULL) free(sources); free_tracewide(traceparam); free_tracewide(wideparam); printf("ERROR: Config fault observed on definitions! \n"); } else { // Construct the digipeater digi = calloc(1,sizeof(*digi)); if (debug>1)printf(" sourcecount=%d\n",sourcecount); // up-link all interfaces used as sources for ( i = 0; i < sourcecount; ++i ) { struct digipeater_source *src = sources[i]; src->parent = digi; // Set parent link src->src_if->digisources = realloc( src->src_if->digisources, (src->src_if->digisourcecount +1) * (sizeof(void*))); src->src_if->digisources[src->src_if->digisourcecount] = src; src->src_if->digisourcecount += 1; } aif->txrefcount += 1; // Increment Tx usage Reference count. // We permit only one to // use any given Tx-interface. (Rx:es // permit multiple uses.) digi->transmitter = aif; digi->tbf_limit = (ratelimit * TOKENBUCKET_INTERVAL)/60; digi->tbf_increment = (rateincrement * TOKENBUCKET_INTERVAL)/60; digi->src_tbf_limit = (srcratelimit * TOKENBUCKET_INTERVAL)/60; digi->src_tbf_increment = (srcrateincrement * TOKENBUCKET_INTERVAL)/60; digi->tokenbucket = digi->tbf_limit; digi->dupechecker = dupecheck_new(dupestoretime); // Dupecheck is per transmitter #ifndef DISABLE_IGATE digi->historydb = historydb_new(); // HistoryDB is per transmitter #endif digi->trace = (traceparam != NULL) ? traceparam : & default_trace_param; digi->wide = (wideparam != NULL) ? wideparam : & default_wide_param; digi->sourcecount = sourcecount; digi->sources = sources; digis = realloc( digis, sizeof(void*) * (digi_count+1)); digis[digi_count] = digi; ++digi_count; } return has_fault; } static int decrement_ssid(uint8_t *ax25addr) { // bit-field manipulation int ssid = (ax25addr[AX25ADDRLEN-1] >> 1) & 0x0F; if (ssid > 0) --ssid; ax25addr[AX25ADDRLEN-1] = (ax25addr[AX25ADDRLEN-1] & 0xE1) | (ssid << 1); return ssid; } /* 0 == accept, otherwise reject */ /* int digipeater_receive_filter(struct digipeater_source *src, struct pbuf_t *pb) { if (src->src_filters == NULL) { if (debug>1) printf("No source filters, accepted the packet from %s.\n", src->src_if->callsign); return 0; } int rc = filter_process(pb, src->src_filters, src->parent->historydb); if (rc != 1) { if (debug>1) printf("Source filtering rejected the packet from %s.\n", src->src_if->callsign); return 1; } if (debug>1) printf("Source filtering accepted the packet from %s.\n", src->src_if->callsign); return 0; } */ static void digipeater_receive_backend(struct digipeater_source *src, struct pbuf_t *pb) { int len, viaindex; struct digistate state; struct viastate viastate; struct digipeater *digi = src->parent; char viafield[14]; // room for text format uint8_t *axaddr, *e; memset(&state, 0, sizeof(state)); memset(&viastate, 0, sizeof(viastate)); // 2) Verify that none of our interface callsigns does match any // of already DIGIPEATED via fields! (fields that have H-bit set) // ( present implementation: this digi's transmitter callsign is // verified) // Parse executed and requested WIDEn-N/TRACEn-N info if (parse_tnc2_hops(&state, src, pb)) { // A fault was observed! -- tests include "not this transmitter" if (debug>1) printf("Parse_tnc2_hops rejected this."); return; } if (pb->is_aprs) { if (state.v.probably_heard_direct) { // Collect a decaying average of distances to stations? // .. could auto-beacon an aloha-circle - maybe // .. note: this does not get packets that have no VIA fields. // Score of direct DX:es? // .. note: this does not get packets that have no VIA fields. } else { if (src->src_relaytype == DIGIRELAY_DIGIPEAT_DIRECTONLY) { // Source relaytype is DIRECTONLY, and this was not // likely directly heard... if (debug>1) printf("DIRECTONLY -mode, and packet is probably not direct heard."); return; } } // Keep score of all DX packets? if (try_reject_filters(3, pb->info_start, src)) { if (debug>1) printf(" - Data body regexp filters reject\n"); return; // data body regexp reject filters } // FIXME: 3) aprsc style filters checking in service area of the packet.. } // 4) Hop-count filtering: // APRSIS sourced packets have different rules than DIGIPEAT // packets... if (state.v.hopsreq <= state.v.hopsdone) { if (debug>1) printf(" No remaining hops to execute.\n"); return; } if (state.v.hopsreq > digi->trace->maxreq || state.v.hopsreq > digi->wide->maxreq || state.v.tracereq > digi->trace->maxreq || state.v.hopsdone > digi->trace->maxdone || state.v.hopsdone > digi->wide->maxdone || state.v.tracedone > digi->trace->maxdone) { if (debug) printf(" Packet exceeds digipeat limits\n"); if (!state.v.probably_heard_direct) { if (debug) printf(".. discard.\n"); return; } else { state.v.fixall = 1; } } // if (debug) printf(" Packet accepted to digipeat!\n"); state.ax25addrlen = pb->ax25addrlen; memcpy(state.ax25addr, pb->ax25addr, pb->ax25addrlen); axaddr = state.ax25addr + 2*AX25ADDRLEN; e = state.ax25addr + state.ax25addrlen; if (state.v.fixall) { // Okay, insert my transmitter callsign on the first // VIA field, and mark the rest with H-bit // (in search loop below) int taillen = e - axaddr; if (state.ax25addrlen >= AX25ADDRMAXLEN) { if (debug) printf(" FIXALL TRACE overgrows the VIA fields! Dropping last of incoming ones.\n"); // Drop the last via field to make room for insert below. state.ax25addrlen -= AX25ADDRLEN; taillen -= AX25ADDRLEN; } // If we have a tail, move it up (there is always room for it) if (taillen > 0) memmove(axaddr+AX25ADDRLEN, axaddr, taillen); state.ax25addrlen += AX25ADDRLEN; e = state.ax25addr + state.ax25addrlen; // recalculate! // Put the transmitter callsign in memcpy(axaddr, digi->transmitter->ax25call, AX25ADDRLEN); // Set Address Termination bit at the last VIA field // (possibly ours, or maybe the previous one was truncated..) axaddr[state.ax25addrlen-1] |= AX25ATERM; } // Search for first AX.25 VIA field that does not have H-bit set: viaindex = 1; // First via field is number 2 *viafield = 0; // clear that buffer for starters for (; axaddr < e; axaddr += AX25ADDRLEN, ++viaindex) { ax25_to_tnc2_fmtaddress(viafield, axaddr, 0); // if (debug>1) { // printf(" via: %s", viafield); // } // Initial parsing said that things are seriously wrong.. // .. and we will digipeat the packet with all H-bits set. if (state.v.fixall) axaddr[AX25ADDRLEN-1] |= AX25HBIT; if (!(axaddr[AX25ADDRLEN-1] & AX25HBIT)) // No "Has Been Digipeated" bit set break; // this doesn't happen in "fixall" mode } switch (src->src_relaytype) { case DIGIRELAY_THIRDPARTY: // Effectively disable the digipeat modifying of address axaddr = e; break; case DIGIRELAY_DIGIPEAT: // Normal functionality break; default: ; } // Unprocessed VIA field found (not in FIXALL mode) if (axaddr < e) { // VIA-field of interest has been found // FIXME: 5) / 6) Cross-frequency/cross-band digipeat may add a special // label telling that the message originated on other band // 7) WIDEn-N treatment (as well as transmitter matching digi) if (pb->digi_like_aprs) { if (strcmp(viafield,digi->transmitter->callsign) == 0 || // Match on the transmitter callsign without the star... match_aliases(viafield, digi->transmitter)) { // .. or match transmitter interface alias. // Treat it as a TRACE request. int aterm = axaddr[AX25ADDRLEN-1] & AX25ATERM; // save old address termination bit // Put the transmitter callsign in, and set the H-bit. memcpy(axaddr, digi->transmitter->ax25call, AX25ADDRLEN); axaddr[AX25ADDRLEN-1] |= (AX25HBIT | aterm); // Set H-bit } else if ((len = match_tracewide(viafield, src->src_trace))) { count_single_tnc2_tracewide(&viastate, viafield, 1, len, viaindex); } else if ((len = match_tracewide(viafield, digi->trace))) { count_single_tnc2_tracewide(&viastate, viafield, 1, len, viaindex); } else if ((len = match_tracewide(viafield, src->src_wide))) { count_single_tnc2_tracewide(&viastate, viafield, 0, len, viaindex); } else if ((len = match_tracewide(viafield, digi->wide))) { count_single_tnc2_tracewide(&viastate, viafield, 0, len, viaindex); } } else { // Not "digi_as_aprs" rules if (strcmp(viafield,digi->transmitter->callsign) == 0) { // Match on the transmitter callsign without the star. // Treat it as a TRACE request. int aterm = axaddr[AX25ADDRLEN-1] & AX25ATERM; // save old address termination bit // Put the transmitter callsign in, and set the H-bit. memcpy(axaddr, digi->transmitter->ax25call, AX25ADDRLEN); axaddr[AX25ADDRLEN-1] |= (AX25HBIT | aterm); // Set H-bit } else if (match_aliases(viafield, digi->transmitter)) { // Match on the aliases. // Treat it as a TRACE request. int aterm = axaddr[AX25ADDRLEN-1] & AX25ATERM; // save old address termination bit // Put the transmitter callsign in, and set the H-bit. memcpy(axaddr, digi->transmitter->ax25call, AX25ADDRLEN); axaddr[AX25ADDRLEN-1] |= (AX25HBIT | aterm); // Set H-bit } } if (viastate.tracereq > viastate.tracedone) { // if (debug) printf(" TRACE on %s!\n",viafield); // Must move it up in memory to be able to put // transmitter callsign in int taillen = e - axaddr; int newssid; if (state.ax25addrlen >= AX25ADDRMAXLEN) { if (debug) printf(" TRACE overgrows the VIA fields! Discard.\n"); return; } memmove(axaddr+AX25ADDRLEN, axaddr, taillen); state.ax25addrlen += AX25ADDRLEN; newssid = decrement_ssid(axaddr+AX25ADDRLEN); if (newssid <= 0) axaddr[2*AX25ADDRLEN-1] |= AX25HBIT; // Set H-bit // Put the transmitter callsign in, and set the H-bit. memcpy(axaddr, digi->transmitter->ax25call, AX25ADDRLEN); axaddr[AX25ADDRLEN-1] |= AX25HBIT; // Set H-bit } else if (viastate.hopsreq > viastate.hopsdone) { // If configuration didn't process "WIDE" et.al. as // a TRACE, then here we process them without trace.. int newssid; if (debug) printf(" VIA on %s!\n",viafield); newssid = decrement_ssid(axaddr); if (newssid <= 0) axaddr[AX25ADDRLEN-1] |= AX25HBIT; // Set H-bit } } { history_cell_t *hcell; char tbuf[2800]; int is_ui = 0, ui_pid = -1, frameaddrlen = 0, tnc2addrlen = 0, t2l; // uint8_t *u = state.ax25addr + state.ax25addrlen; // *u++ = 0; // *u++ = 0; // *u++ = 0; t2l = ax25_format_to_tnc( state.ax25addr, state.ax25addrlen+AX25ADDRLEN-1, tbuf, sizeof(tbuf), & frameaddrlen, &tnc2addrlen, & is_ui, &ui_pid ); tbuf[t2l] = 0; if (debug) { printf(" out-hdr: '%s' data='",tbuf); (void)fwrite(pb->ax25data+2, pb->ax25datalen-2, // without Control+PID 1, stdout); printf("'\n"); } #ifndef DISABLE_IGATE // Insert into history database - track every packet hcell = historydb_insert_( digi->historydb, pb, 1 ); if (hcell != NULL) { if (hcell->tokenbucket < 1.0) { if (debug) printf("TRANSMITTER SOURCE CALLSIGN RATELIMIT DISCARD.\n"); return; } hcell->tokenbucket -= 1.0; } #endif // Now we do token bucket filtering -- rate limiting if (digi->tokenbucket < 1.0) { if (debug) printf("TRANSMITTER RATELIMIT DISCARD.\n"); return; } digi->tokenbucket -= 1.0; if (pb->is_aprs && rflogfile) { int t2l2; // Essentially Debug logging.. to file if (sizeof(tbuf) - pb->ax25datalen > t2l && t2l > 0) { // Have space for body too, skip leading Ctrl+PID bytes memcpy(tbuf+t2l, pb->ax25data+2, pb->ax25datalen-2); // Ctrl+PID skiped t2l2 = t2l + pb->ax25datalen-2; // tbuf size sans Ctrl+PID rflog( digi->transmitter->callsign, 'T', 0, tbuf, t2l2 ); tbuf[t2l]=0; } } // Feed to dupe-filter (transmitter specific) // this means we have already seen it, and when // it comes back from somewhere, we do not digipeat // it ourselves. // This recording is needed at output side of digipeater // for APRSIS and DPRS transmit gates. if (t2l>0) { dupecheck_aprs( digi->dupechecker, (const char *)tbuf, t2l, (const char *)pb->ax25data+2, pb->ax25datalen-2 ); // ignore Ctrl+PID } else { dupecheck_aprs( digi->dupechecker, (const char *)state.ax25addr, state.ax25addrlen, (const char *)pb->ax25data+2, pb->ax25datalen-2 ); // ignore Ctrl+PID } } // Feed to interface_transmit_ax25() with new header and body interface_transmit_ax25( digi->transmitter, state.ax25addr, state.ax25addrlen, (const char*)pb->ax25data, pb->ax25datalen ); if (debug>1) printf("Done.\n"); } void digipeater_receive( struct digipeater_source *src, struct pbuf_t *pb ) { // Below numbers like "4)" refer to Requirement Specification // paper chapter 2.6: Digipeater Rules // The dupe-filter exists for APRS frames, possibly for some // selected UI frame types, and definitely not for CONS frames. if (debug) printf("digipeater_receive() from %s, is_aprs=%d viscous_delay=%d\n", src->src_if->callsign, pb->is_aprs, src->viscous_delay); if (src->tokenbucket < 1.0) { if (debug) printf("SOURCE RATELIMIT DISCARD\n"); return; } src->tokenbucket -= 1.0; if (pb->is_aprs) { const int source_is_transmitter = (src->src_if == src->parent->transmitter); // 1) Feed to dupe-filter (transmitter specific) // If the dupe detector on this packet has reached // count > 1, drop it. int jittery = src->viscous_delay > 0 ? random() % 3 + src->viscous_delay : 0; dupe_record_t *dupe = dupecheck_pbuf( src->parent->dupechecker, pb, jittery); if (dupe == NULL) { // Oops.. allocation error! if (debug) printf("digipeater_receive() - dupecheck_pbuf() allocation error, packet discarded\n"); return; } // 1.1) optional viscous delay! if (src->viscous_delay == 0) { // No delay, direct cases // First packet on direct source arrives here // with seen = 1 // 1.x) Analyze dupe checking if (debug>1) printf("Seen this packet %d times (delayed=%d)\n", dupe->delayed_seen + dupe->seen, dupe->delayed_seen); if (dupe->seen > 1) { // N:th direct packet, duplicate. // Drop this direct packet. if (debug>1) printf(".. discarded\n"); return; } if (dupe->seen == 1 && dupe->delayed_seen > 0 && dupe->pbuf == NULL) { // First direct, but dupe record does not have // pbuf anymore indicating that a delayed // handling did process it sometime in past. // Drop this direct packet. if (debug>1) printf(".. discarded\n"); return; } if (dupe->seen == 1 && dupe->delayed_seen >= 0 && dupe->pbuf != NULL) { // First direct, and pbuf exists in dupe record. // It was added first to viscous queue, and // a bit latter came this direct one. // Remove one from viscous queue, and proceed // with direct processing. if (debug>1) printf(" .. discard dupe record, process immediately"); pbuf_put(dupe->pbuf); dupe->pbuf = NULL; dupe = NULL; // Do not do dupecheck_put() here! } } else { // src->viscous_delay > 0 // First packet on viscous source arrives here // with dupe->delayed_seen = 1 // Has this been seen on direct channel? if (dupe->seen > 0) { // Already processed thru direct processing, // no point in adding this to viscous delay queue if (debug>1) printf("Seen this packet %d times. Discarding it.\n", dupe->delayed_seen + dupe->seen); return; } // Depending on source definition, the transmitter is // either non-viscous or viscous. We care about it // only when the source is viscous: if (source_is_transmitter) dupe->seen_on_transmitter += 1; if (dupe->delayed_seen > 1) { // 2nd or more of same packet from delayed source if (debug>1) printf("Seen this packet %d times.\n", dupe->delayed_seen + dupe->seen); // If any of them is transmitter interface, then // drop the queued packet, and drop current one. if (dupe->seen_on_transmitter > 0) { // If pbuf is on delayed queue, drop it. if (dupe->pbuf != NULL) { pbuf_put(dupe->pbuf); dupe->pbuf = NULL; dupe = NULL; // Do not do dupecheck_put() here! } } if (debug>1) printf(".. discarded\n"); return; } // First time that we have seen this packet at all. // Put the pbuf_t on viscous delay queue.. (Put // this dupe_record_t there, and the pbuf_t pointer // is already in that dupe_record_t.) src->viscous_queue_size += 1; if (src->viscous_queue_size > src->viscous_queue_space) { src->viscous_queue_space += 16; src->viscous_queue = realloc( src->viscous_queue, sizeof(void*) * src->viscous_queue_space ); } src->viscous_queue[ src->viscous_queue_size -1 ] = dupecheck_get(dupe); if (debug) printf("%ld ENTER VISCOUS QUEUE: len=%d pbuf=%p\n", tick.tv_sec, src->viscous_queue_size, pb); return; // Put on viscous queue } } // Send directly to backend if (debug>1) printf(".. direct to processing\n"); digipeater_receive_backend(src, pb); } dupecheck_t *digipeater_find_dupecheck(const struct aprx_interface *aif) { int i; for (i = 0; i < digi_count; ++i) { if (aif == digis[i]->transmitter) return digis[i]->dupechecker; } return NULL; } static void digipeater_resettime(void *arg) { struct timeval *tv = (struct timeval *)arg; *tv = tick; } // Viscous queue processing needs poll digis s for delayed actions int digipeater_prepoll(struct aprxpolls *app) { int d, s; if (tokenbucket_timer.tv_sec == 0) { tokenbucket_timer = tick; // init this.. } // If the time(2) has jumped around a lot, // and we didn't get around to do our work, reset the timer. if (time_reset) { digipeater_resettime(&tokenbucket_timer); } if (tv_timercmp( &tokenbucket_timer, &tick ) <= 0) { // Run the digipeater timer handling now // Will also advance the timer! if (debug>2) printf("digipeater_prepoll() run tokenbucket_timers\n"); tv_timeradd_seconds( &tokenbucket_timer, &tokenbucket_timer, TOKENBUCKET_INTERVAL); run_tokenbucket_timers(); } if (tv_timercmp( &tokenbucket_timer, &app->next_timeout ) <= 0) { app->next_timeout = tokenbucket_timer; } // if (debug>2) printf("digipeater_prepoll - 1 - timeout millis=%d\n",aprxpolls_millis(app)); // Over all digipeaters.. for (d = 0; d < digi_count; ++d) { struct digipeater *digi = digis[d]; // Over all sources in those digipeaters for (s = 0; s < digi->sourcecount; ++s) { struct timeval tv; struct digipeater_source * src = digi->sources[s]; // If viscous delay is zero, there is no work... // if (src->viscous_delay == 0) // continue; // Delay is non-zero, perhaps there is work? if (src->viscous_queue_size == 0) // Empty queue continue; // First entry expires first tv.tv_sec = src->viscous_queue[0]->t + src->viscous_delay; tv.tv_usec = 0; if (tv_timercmp(&app->next_timeout, &tv) > 0) { app->next_timeout = tv; // if (debug>2) printf("digipeater_prepoll - 2 - timeout millis=%d\n",aprxpolls_millis(app)); } } } return 0; } static void sourcecalltick(struct digipeater *digi); int digipeater_postpoll(struct aprxpolls *app) { int d, s, i, donecount; if (tv_timercmp(&tokenbucket_timer, &tick) < 0) { tv_timeradd_seconds( &tokenbucket_timer, &tokenbucket_timer, TOKENBUCKET_INTERVAL); run_tokenbucket_timers(); } // Over all digipeaters.. for (d = 0; d < digi_count; ++d) { struct digipeater *digi = digis[d]; // Over all sources in those digipeaters for (s = 0; s < digi->sourcecount; ++s) { struct digipeater_source * src = digi->sources[s]; // If viscous delay is zero, there is no work... // if (src->viscous_delay == 0) // continue; // Delay is non-zero, perhaps there is work? if (src->viscous_queue_size == 0) // Empty queue continue; // Feed backend from viscous queue donecount = 0; for (i = 0; i < src->viscous_queue_size; ++i) { struct dupe_record_t *dupe = src->viscous_queue[i]; time_t t = dupe->t + src->viscous_delay; if ((t - tick.tv_sec) <= 0) { if (debug)printf("%ld LEAVE VISCOUS QUEUE: dupe=%p pbuf=%p\n", tick.tv_sec, dupe, dupe->pbuf); if (dupe->pbuf != NULL) { // We send the pbuf from viscous queue, if it still is // present in the dupe record. (For example direct sourced // packets remove a packet from queued dupe record.) digipeater_receive_backend(src, dupe->pbuf); // Remove the delayed pbuf from this dupe record. pbuf_put(dupe->pbuf); dupe->pbuf = NULL; } dupecheck_put(dupe); ++donecount; } else { break; // found a case we are not yet interested in. } } if (donecount > 0) { if (donecount >= src->viscous_queue_size) { // All cleared src->viscous_queue_size = 0; } else { // Compact the queue left after this processing round i = src->viscous_queue_size - donecount; memcpy(&src->viscous_queue[0], &src->viscous_queue[donecount], sizeof(void*) * i); src->viscous_queue_size = i; } } } } return 0; } static int run_tokenbucket_timers() { int d, s; // Over all digipeaters.. for (d = 0; d < digi_count; ++d) { struct digipeater *digi = digis[d]; digi->tokenbucket += digi->tbf_increment; if (digi->tokenbucket > digi->tbf_limit) digi->tokenbucket = digi->tbf_limit; #ifndef DISABLE_IGATE sourcecalltick(digi); #endif // Over all sources in those digipeaters for (s = 0; s < digi->sourcecount; ++s) { struct digipeater_source * src = digi->sources[s]; src->tokenbucket += src->tbf_increment; if (src->tokenbucket > src->tbf_limit) src->tokenbucket = src->tbf_limit; } } return 0; } #ifndef DISABLE_IGATE static void sourcecalltick(struct digipeater *digi) { int i; historydb_t *db = digi->historydb; if (db == NULL) return; // Should never happen.. for (i = 0; i < HISTORYDB_HASH_MODULO; ++i) { history_cell_t *c = db->hash[i]; for ( ; c != NULL; c = c->next ) { c->tokenbucket += digi->src_tbf_increment; if (c->tokenbucket > digi->src_tbf_limit) c->tokenbucket = digi->src_tbf_limit; } } } #endif // An utility function that exists at GNU Libc.. #if !defined(HAVE_MEMRCHR) && !defined(_FOR_VALGRIND_) void *memrchr(const void *s, int c, size_t n) { const unsigned char *p = s; c &= 0xFF; for (p = s+n; n > 0; --n, --p) { if (*p == c) return (void*)p; } return NULL; } #endif aprx-2.08.svn593/parse_aprs.c0000644000175000017500000011264412306656055015000 0ustar colincolin/* * aprsc * * (c) Heikki Hannikainen, OH7LZB * * This program is licensed under the BSD license, which can be found * in the file LICENSE. * */ /* * A simple APRS parser for aprsc. Translated from Ham::APRS::FAP * perl module (by OH2KKU). * * Only needs to get lat/lng out of the packet, other features would * be unnecessary in this application, and slow down the parser. * ... but lets still classify the packet, output filter needs that. * */ #include "aprx.h" #include #define DEBUG_LOG(...) if(debug)printf(__VA_ARGS__) /* * Check if the given character is a valid symbol table identifier * or an overlay character. The set is different for compressed * and uncompressed packets - the former has the overlaid number (0-9) * replaced with n-j. */ static int valid_sym_table_compressed(char c) { return (c == '/' || c == '\\' || (c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x6A)); /* [\/\\A-Za-j] */ } static int valid_sym_table_uncompressed(char c) { return (c == '/' || c == '\\' || (c >= 0x41 && c <= 0x5A) || (c >= 0x30 && c <= 0x39)); /* [\/\\A-Z0-9] */ } /* * Fill the pbuf_t structure with a parsed position and * symbol table & code. Also does range checking for lat/lng * and pre-calculates cosf(lat) for range filters. */ static int pbuf_fill_pos(struct pbuf_t *pb, const float lat, const float lng, const char sym_table, const char sym_code) { int bad = 0; /* symbol table and code */ pb->symbol[0] = sym_table; pb->symbol[1] = sym_code; pb->symbol[2] = 0; /* Is it perhaps a weather report ? */ if (sym_code == '_' && (sym_table == '/' || sym_table == '\\')) pb->packettype |= T_WX; if (sym_code == '@' && (sym_table == '/' || sym_table == '\\')) pb->packettype |= T_WX; /* Hurricane */ bad |= (lat < -89.9 && -0.0001 <= lng && lng <= 0.0001); bad |= (lat > 89.9 && -0.0001 <= lng && lng <= 0.0001); if (-0.0001 <= lat && lat <= 0.0001) { bad |= ( -0.0001 <= lng && lng <= 0.0001); bad |= ( -90.01 <= lng && lng <= -89.99); bad |= ( 89.99 <= lng && lng <= 90.01); } if (bad || lat < -90.0 || lat > 90.0 || lng < -180.0 || lng > 180.0) { if (debug) printf("\tposition out of range: lat %.3f lng %.3f", lat, lng); return 0; /* out of range */ } if (debug) printf("\tposition ok: lat %.3f lng %.3f", lat, lng); /* Pre-calculations for A/R/F/M-filter tests */ pb->lat = filter_lat2rad(lat); /* deg-to-radians */ pb->cos_lat = cosf(pb->lat); /* used in range filters */ pb->lng = filter_lon2rad(lng); /* deg-to-radians */ pb->flags |= F_HASPOS; /* the packet has positional data */ return 1; } /* * Parse symbol from destination callsign */ static int get_symbol_from_dstcall_twochar(const char c1, const char c2, char *sym_table, char *sym_code) { //hlog(LOG_DEBUG, "\ttwochar %c %c", c1, c2); if (c1 == 'B') { if (c2 >= 'B' && c2 <= 'P') { *sym_table = '/'; *sym_code = c2 - 'B' + '!'; return 1; } return 0; } if (c1 == 'P') { if (c2 >= '0' && c2 <= '9') { *sym_table = '/'; *sym_code = c2; return 1; } if (c2 >= 'A' && c2 <= 'Z') { *sym_table = '/'; *sym_code = c2; return 1; } return 0; } if (c1 == 'M') { if (c2 >= 'R' && c2 <= 'X') { *sym_table = '/'; *sym_code = c2 - 'R' + ':'; return 1; } return 0; } if (c1 == 'H') { if (c2 >= 'S' && c2 <= 'X') { *sym_table = '/'; *sym_code = c2 - 'S' + '['; return 1; } return 0; } if (c1 == 'L') { if (c2 >= 'A' && c2 <= 'Z') { *sym_table = '/'; *sym_code = c2 - 'A' + 'a'; return 1; } return 0; } if (c1 == 'J') { if (c2 >= '1' && c2 <= '4') { *sym_table = '/'; *sym_code = c2 - '1' + '{'; return 1; } return 0; } if (c1 == 'O') { if (c2 >= 'B' && c2 <= 'P') { *sym_table = '\\'; *sym_code = c2 - 'B' + '!'; return 1; } return 0; } if (c1 == 'A') { if (c2 >= '0' && c2 <= '9') { *sym_table = '\\'; *sym_code = c2; return 1; } if (c2 >= 'A' && c2 <= 'Z') { *sym_table = '\\'; *sym_code = c2; return 1; } return 0; } if (c1 == 'N') { if (c2 >= 'R' && c2 <= 'X') { *sym_table = '\\'; *sym_code = c2 - 'R' + ':'; return 1; } return 0; } if (c1 == 'D') { if (c2 >= 'S' && c2 <= 'X') { *sym_table = '\\'; *sym_code = c2 - 'S' + '['; return 1; } return 0; } if (c1 == 'S') { if (c2 >= 'A' && c2 <= 'Z') { *sym_table = '\\'; *sym_code = c2 - 'A' + 'a'; return 1; } return 0; } if (c1 == 'Q') { if (c2 >= '1' && c2 <= '4') { *sym_table = '\\'; *sym_code = c2 - '1' + '{'; return 1; } return 0; } return 0; } static int get_symbol_from_dstcall(struct pbuf_t *pb, char *sym_table, char *sym_code) { const char *d_start; char type; char overlay; int sublength; int numberid; /* check that the destination call exists and is of the right size for symbol */ d_start = pb->srccall_end+1; if (pb->dstcall_end_or_ssid - d_start < 5) return 0; /* too short */ /* length of the parsed string */ sublength = pb->dstcall_end_or_ssid - d_start - 3; if (sublength > 3) sublength = 3; #ifdef DEBUG_PARSE_APRS if (debug) printf("\tget_symbol_from_dstcall: %.*s (%d)", (int)(pb->dstcall_end_or_ssid - d_start), d_start, sublength); #endif if (strncmp(d_start, "GPS", 3) != 0 && strncmp(d_start, "SPC", 3) != 0 && strncmp(d_start, "SYM", 3) != 0) return 0; // hlog(LOG_DEBUG, "\ttesting %c %c %c", d_start[3], d_start[4], d_start[5]); if (!isalnum(d_start[3]) || !isalnum(d_start[4])) return 0; if (sublength == 3 && !isalnum(d_start[5])) return 0; type = d_start[3]; if (sublength == 3) { if (type == 'C' || type == 'E') { if (!isdigit(d_start[4])) return 0; if (!isdigit(d_start[5])) return 0; numberid = (d_start[4] - 48) * 10 + (d_start[5] - 48); *sym_code = numberid + 32; if (type == 'C') *sym_table = '/'; else *sym_table = '\\'; #ifdef DEBUG_PARSE_APRS if (debug) printf("\tnumeric symbol id in dstcall: %.*s: table %c code %c", (int)(pb->dstcall_end_or_ssid - d_start - 3), d_start + 3, *sym_table, *sym_code); #endif return 1; } else { /* secondary symbol table, with overlay * Check first that we really are in the secondary symbol table */ overlay = d_start[5]; if ((type == 'O' || type == 'A' || type == 'N' || type == 'D' || type == 'S' || type == 'Q') && isalnum(overlay)) { return get_symbol_from_dstcall_twochar(d_start[3], d_start[4], sym_table, sym_code); } return 0; } } else { // primary or secondary table, no overlay return get_symbol_from_dstcall_twochar(d_start[3], d_start[4], sym_table, sym_code); } return 0; } /* * Parse NMEA position packets. */ static int parse_aprs_nmea(struct pbuf_t *pb, const char *body, const char *body_end) { float lat, lng; const char *latp, *lngp; int i, la, lo; char lac, loc; char sym_table, sym_code; if (memcmp(body,"ULT",3) == 0) { /* Ah.. "$ULT..." - that is, Ultimeter 2000 weather instrument */ pb->packettype |= T_WX; return 1; } lat = lng = 0.0; latp = lngp = NULL; /* NMEA sentences to understand: $GPGGA Global Positioning System Fix Data $GPGLL Geographic Position, Latitude/Longitude Data $GPRMC Remommended Minimum Specific GPS/Transit Data $GPWPT Way Point Location ?? (bug in APRS specs ?) $GPWPL Waypoint Load (not in APRS specs, but in NMEA specs) $PNTS Seen on APRS-IS, private sentense based on NMEA.. $xxTLL Not seen on radio network, usually $RATLL - Target positions reported by RAdar. */ if (memcmp(body, "GPGGA,", 6) == 0) { /* GPGGA,175059,3347.4969,N,11805.7319,W,2,12,1.0,6.8,M,-32.1,M,,*7D // v=1, looks fine // GPGGA,000000,5132.038,N,11310.221,W,1,09,0.8,940.0,M,-17.7,, // v=1, timestamp odd, coords look fine // GPGGA,,,,,,0,00,,,,,,,*66 // v=0, invalid // GPGGA,121230,4518.7931,N,07322.3202,W,2,08,1.0,40.0,M,-32.4,M,,*46 // v=2, looks valid ? // GPGGA,193115.00,3302.50182,N,11651.22581,W,1,08,01.6,00465.90,M,-32.891,M,,*5F // $GPGGA,hhmmss.dd,xxmm.dddd,,yyymm.dddd,,v, // ss,d.d,h.h,M,g.g,M,a.a,xxxx*hh */ latp = body+6; // over the keyword while (latp < body_end && *latp != ',') latp++; // scan over the timestamp if (*latp == ',') latp++; // .. and into latitude. lngp = latp; while (lngp < body_end && *lngp != ',') lngp++; if (*lngp == ',') lngp++; if (*lngp != ',') lngp++; if (*lngp == ',') lngp++; /* latp, and lngp point to start of latitude and longitude substrings // respectively. */ } else if (memcmp(body, "GPGLL,", 6) == 0) { /* $GPGLL,xxmm.dddd,,yyymm.dddd,,hhmmss.dd,S,M*hh */ latp = body+6; // over the keyword lngp = latp; while (lngp < body_end && *lngp != ',') // over latitude lngp++; if (*lngp == ',') lngp++; // and lat designator if (*lngp != ',') lngp++; // and lat designator if (*lngp == ',') lngp++; /* latp, and lngp point to start of latitude and longitude substrings // respectively */ } else if (memcmp(body, "GPRMC,", 6) == 0) { /* $GPRMC,hhmmss.dd,S,xxmm.dddd,,yyymm.dddd,,s.s,h.h,ddmmyy,d.d, ,M*hh // ,S, = Status: 'A' = Valid, 'V' = Invalid // // GPRMC,175050,A,4117.8935,N,10535.0871,W,0.0,324.3,100208,10.0,E,A*3B // GPRMC,000000,V,0000.0000,0,00000.0000,0,000,000,000000,,*01/It wasn't me :) // invalid.. // GPRMC,000043,V,4411.7761,N,07927.0448,W,0.000,0.0,290697,10.7,W*57 // GPRMC,003803,A,3347.1727,N,11812.7184,W,000.0,000.0,140208,013.7,E*67 // GPRMC,050058,A,4609.1143,N,12258.8184,W,0.000,0.0,100208,18.0,E*5B */ latp = body+6; // over the keyword while (latp < body_end && *latp != ',') latp++; // scan over the timestamp if (*latp == ',') latp++; // .. and into VALIDITY if (*latp != 'A' && *latp != 'V') return 0; // INVALID ! if (*latp != ',') latp++; if (*latp == ',') latp++; /* now it points to latitude substring */ lngp = latp; while (lngp < body_end && *lngp != ',') lngp++; if (*lngp == ',') lngp++; if (*lngp != ',') lngp++; if (*lngp == ',') lngp++; /* latp, and lngp point to start of latitude and longitude substrings // respectively. */ } else if (memcmp(body, "GPWPL,", 6) == 0) { /* $GPWPL,4610.586,N,00607.754,E,4*70 // $GPWPL,4610.452,N,00607.759,E,5*74 */ latp = body+6; } else if (memcmp(body, "PNTS,1,", 7) == 0) { /* PNTS version 1 */ /* $PNTS,1,0,11,01,2002,231932,3539.687,N,13944.480,E,0,000,5,Roppongi UID RELAY,000,1*35 // $PNTS,1,0,14,01,2007,131449,3535.182,N,13941.200,E,0,0.0,6,Oota-Ku KissUIDigi,000,1*1D // $PNTS,1,0,17,02,2008,120824,3117.165,N,13036.481,E,49,059,1,Kagoshima,000,1*71 // $PNTS,1,0,17,02,2008,120948,3504.283,N,13657.933,E,00,000.0,6,,000,1*36 // // From Alinco EJ-41U Terminal Node Controller manual: // // 5-4-7 $PNTS // This is a private-sentence based on NMEA-0183. The data contains date, // time, latitude, longitude, moving speed, direction, altitude plus a short // message, group codes, and icon numbers. The EJ-41U does not analyze this // format but can re-structure it. // The data contains the following information: // l $PNTS Starts the $PNTS sentence // l version // l the registered information. [0]=normal geographical location data. // This is the only data EJ-41U can re-structure. [s]=Initial position // for the course setting [E]=ending position for the course setting // [1]=the course data between initial and ending [P]=the check point // registration [A]=check data when the automatic position transmission // is set OFF [R]=check data when the course data or check point data is // received. // l dd,mm,yyyy,hhmmss: Date and time indication. // l Latitude in DMD followed by N or S // l Longitude in DMD followed by E or W // l Direction: Shown with the number 360 degrees divided by 64. // 00 stands for true north, 16 for east. Speed in Km/h // l One of 15 characters [0] to [9], [A] to [E]. // NTSMRK command determines this character when EJ-41U is used. // l A short message up to 20 bites. Use NTSMSG command to determine this message. // l A group code: 3 letters with a combination of [0] to [9], [A] to [Z]. // Use NTSGRP command to determine. // l Status: [1] for usable information, [0] for non-usable information. // l *hh the check-sum and end of PNTS sentence. */ if (body+55 > body_end) { DEBUG_LOG("body too short"); return 0; /* Too short.. */ } latp = body+7; /* Over the keyword */ /* Accept any registered information code */ if (*latp++ == ',') return 0; if (*latp++ != ',') return 0; /* Scan over date+time info */ while (*latp != ',' && latp <= body_end) ++latp; if (*latp == ',') ++latp; while (*latp != ',' && latp <= body_end) ++latp; if (*latp == ',') ++latp; while (*latp != ',' && latp <= body_end) ++latp; if (*latp == ',') ++latp; while (*latp != ',' && latp <= body_end) ++latp; if (*latp == ',') ++latp; /* now it points to latitude substring */ lngp = latp; while (lngp < body_end && *lngp != ',') lngp++; if (*lngp == ',') lngp++; if (*lngp != ',') lngp++; if (*lngp == ',') lngp++; /* latp, and lngp point to start of latitude and longitude substrings // respectively. */ #if 1 } else if (memcmp(body, "GPGSA,", 6) == 0 || memcmp(body, "GPVTG,", 6) == 0 || memcmp(body, "GPGSV,", 6) == 0) { /* Recognized but ignored */ return 1; #endif } if (!latp || !lngp) { if (debug) fprintf(stderr, "Unknown NMEA: '%.11s' %.*s", pb->data, (int)(body_end - body), body); return 0; /* Well.. Not NMEA frame */ } // hlog(LOG_DEBUG, "NMEA parsing: %.*s", (int)(body_end - body), body); // hlog(LOG_DEBUG, " lat=%.10s lng=%.10s", latp, lngp); i = sscanf(latp, "%2d%f,%c,", &la, &lat, &lac); if (i != 3) return 0; // parse failure i = sscanf(lngp, "%3d%f,%c,", &lo, &lng, &loc); if (i != 3) return 0; // parse failure if (lac != 'N' && lac != 'S' && lac != 'n' && lac != 's') return 0; // bad indicator value if (loc != 'E' && loc != 'W' && loc != 'e' && loc != 'w') return 0; // bad indicator value // hlog(LOG_DEBUG, " lat: %c %2d %7.4f lng: %c %2d %7.4f", // lac, la, lat, loc, lo, lng); lat = (float)la + lat/60.0; lng = (float)lo + lng/60.0; if (lac == 'S' || lac == 's') lat = -lat; if (loc == 'W' || loc == 'w') lng = -lng; pb->packettype |= T_POSITION; // Parse symbol from destination callsign get_symbol_from_dstcall(pb, &sym_table, &sym_code); #ifdef DEBUG_PARSE_APRS if (debug) { printf("\tget_symbol_from_dstcall: %.*s => %c%c", (int)(pb->dstcall_end_or_ssid - pb->srccall_end-1), pb->srccall_end+1, sym_table, sym_code); } #endif return pbuf_fill_pos(pb, lat, lng, sym_table, sym_code); } static int parse_aprs_telem(struct pbuf_t *pb, const char *body, const char *body_end) { // float lat = 0.0, lng = 0.0; DEBUG_LOG("parse_aprs_telem"); //pbuf_fill_pos(pb, lat, lng, 0, 0); return 1; // okay } /* * Parse a MIC-E position packet * * APRS PROTOCOL REFERENCE 1.0.1 Chapter 10, page 42 (52 in PDF) * */ static int parse_aprs_mice(struct pbuf_t *pb, const unsigned char *body, const unsigned char *body_end) { float lat = 0.0, lng = 0.0; unsigned int lat_deg = 0, lat_min = 0, lat_min_frag = 0, lng_deg = 0, lng_min = 0, lng_min_frag = 0; const char *d_start; char dstcall[7]; char *p; char sym_table, sym_code; int posambiguity = 0; int i; DEBUG_LOG("parse_aprs_mice: %.*s", pb->packet_len-2, pb->data); /* check packet length */ if (body_end - body < 8) return 0; /* check that the destination call exists and is of the right size for mic-e */ d_start = pb->srccall_end+1; if (pb->dstcall_end_or_ssid - d_start != 6) { DEBUG_LOG(".. bad destcall length! "); return 0; /* eh...? */ } /* validate destination call: * A-K characters are not used in the last 3 characters * and MNO are never used */ if (debug)printf(" destcall='%6.6s'",d_start); for (i = 0; i < 3; i++) if (!((d_start[i] >= '0' && d_start[i] <= '9') || (d_start[i] >= 'A' && d_start[i] <= 'L') || (d_start[i] >= 'P' && d_start[i] <= 'Z'))) { DEBUG_LOG(".. bad destcall characters in posits 1..3"); return 0; } for (i = 3; i < 6; i++) if (!((d_start[i] >= '0' && d_start[i] <= '9') || (d_start[i] == 'L') || (d_start[i] >= 'P' && d_start[i] <= 'Z'))) { DEBUG_LOG(".. bad destcall characters in posits 4..6"); return 0; } DEBUG_LOG("\tpassed dstcall format check"); /* validate information field (longitude, course, speed and * symbol table and code are checked). Not bullet proof.. * * 0 1 23 4 5 6 7 * /^[\x26-\x7f][\x26-\x61][\x1c-\x7f]{2}[\x1c-\x7d][\x1c-\x7f][\x21-\x7b\x7d][\/\\A-Z0-9]/ */ if (body[0] < 0x26 || body[0] > 0x7f) { DEBUG_LOG("..bad infofield column 1"); return 0; } if (body[1] < 0x26 || body[1] > 0x61) { DEBUG_LOG("..bad infofield column 2"); return 0; } if (body[2] < 0x1c || body[2] > 0x7f) { DEBUG_LOG("..bad infofield column 3"); return 0; } if (body[3] < 0x1c || body[3] > 0x7f) { DEBUG_LOG("..bad infofield column 4"); return 0; } if (body[4] < 0x1c || body[4] > 0x7d) { DEBUG_LOG("..bad infofield column 5"); return 0; } if (body[5] < 0x1c || body[5] > 0x7f) { DEBUG_LOG("..bad infofield column 6"); return 0; } if ((body[6] < 0x21 || body[6] > 0x7b) && body[6] != 0x7d) { DEBUG_LOG("..bad infofield column 7"); return 0; } if (!valid_sym_table_uncompressed(body[7])) { DEBUG_LOG("..bad symbol table entry on column 8"); return 0; } DEBUG_LOG("\tpassed info format check"); /* make a local copy, we're going to modify it */ strncpy(dstcall, d_start, 6); dstcall[6] = 0; /* First do the destination callsign * (latitude, message bits, N/S and W/E indicators and long. offset) * * Translate the characters to get the latitude */ //fprintf(stderr, "\tuntranslated dstcall: %s\n", dstcall); for (p = dstcall; *p; p++) { if (*p >= 'A' && *p <= 'J') *p -= 'A' - '0'; else if (*p >= 'P' && *p <= 'Y') *p -= 'P' - '0'; else if (*p == 'K' || *p == 'L' || *p == 'Z') *p = '_'; } //fprintf(stderr, "\ttranslated dstcall: %s\n", dstcall); // position ambiquity is going to get ignored now, // it's not needed in this application. if (dstcall[5] == '_') { dstcall[5] = '5'; posambiguity = 1; } if (dstcall[4] == '_') { dstcall[4] = '5'; posambiguity = 2; } if (dstcall[3] == '_') { dstcall[3] = '5'; posambiguity = 3; } if (dstcall[2] == '_') { dstcall[2] = '3'; posambiguity = 4; } if (dstcall[1] == '_' || dstcall[0] == '_') { DEBUG_LOG("..bad pos-ambiguity on destcall"); return 0; } // cannot use posamb here // convert to degrees, minutes and decimal degrees, // and then to a float lat if (sscanf(dstcall, "%2u%2u%2u", &lat_deg, &lat_min, &lat_min_frag) != 3) { DEBUG_LOG("\tsscanf failed"); return 0; } lat = (float)lat_deg + (float)lat_min / 60.0 + (float)lat_min_frag / 6000.0; // check the north/south direction and correct the latitude if necessary if (d_start[3] <= 0x4c) lat = 0 - lat; /* Decode the longitude, the first three bytes of the body * after the data type indicator. First longitude degrees, * remember the longitude offset. */ lng_deg = body[0] - 28; if (d_start[4] >= 0x50) lng_deg += 100; if (lng_deg >= 180 && lng_deg <= 189) lng_deg -= 80; else if (lng_deg >= 190 && lng_deg <= 199) lng_deg -= 190; /* Decode the longitude minutes */ lng_min = body[1] - 28; if (lng_min >= 60) lng_min -= 60; /* ... and minute decimals */ lng_min_frag = body[2] - 28; /* apply position ambiguity to longitude */ switch (posambiguity) { case 0: /* use everything */ lng = (float)lng_deg + (float)lng_min / 60.0 + (float)lng_min_frag / 6000.0; break; case 1: /* ignore last number of lng_min_frag */ lng = (float)lng_deg + (float)lng_min / 60.0 + (float)(lng_min_frag - lng_min_frag % 10 + 5) / 6000.0; break; case 2: /* ignore lng_min_frag */ lng = (float)lng_deg + ((float)lng_min + 0.5) / 60.0; break; case 3: /* ignore lng_min_frag and last number of lng_min */ lng = (float)lng_deg + (float)(lng_min - lng_min % 10 + 5) / 60.0; break; case 4: /* minute is unused -> add 0.5 degrees to longitude */ lng = (float)lng_deg + 0.5; break; default: DEBUG_LOG(".. posambiguity code BUG!"); return 0; } /* check the longitude E/W sign */ if (d_start[5] >= 0x50) lng = 0 - lng; /* save the symbol table and code */ sym_code = body[6]; sym_table = body[7]; /* ok, we're done */ /* fprintf(stderr, "\tlat %u %u.%u (%.4f) lng %u %u.%u (%.4f)\n", lat_deg, lat_min, lat_min_frag, lat, lng_deg, lng_min, lng_min_frag, lng); fprintf(stderr, "\tsym '%c' '%c'\n", sym_table, sym_code); */ return pbuf_fill_pos(pb, lat, lng, sym_table, sym_code); } /* * Parse a compressed APRS position packet * * APRS PROTOCOL REFERENCE 1.0.1 Chapter 9, page 36 (46 in PDF) * */ static int parse_aprs_compressed(struct pbuf_t *pb, const char *body, const char *body_end) { char sym_table, sym_code; int i; int lat1, lat2, lat3, lat4; int lng1, lng2, lng3, lng4; float lat, lng; DEBUG_LOG("parse_aprs_compressed"); /* A compressed position is always 13 characters long. * Make sure we get at least 13 characters and that they are ok. * Also check the allowed base-91 characters at the same time. */ if (body_end - body < 13) { DEBUG_LOG("\ttoo short"); return 0; /* too short. */ } sym_table = body[0]; /* has been validated before entering this function */ sym_code = body[9]; /* base-91 check */ for (i = 1; i <= 8; i++) if (body[i] < 0x21 || body[i] > 0x7b) return 0; // fprintf(stderr, "\tpassed length and format checks, sym %c%c\n", sym_table, sym_code); /* decode */ lat1 = (body[1] - 33); lat2 = (body[2] - 33); lat3 = (body[3] - 33); lat4 = (body[4] - 33); lat1 = ((((lat1 * 91) + lat2) * 91) + lat3) * 91 + lat4; lng1 = (body[5] - 33); lng2 = (body[6] - 33); lng3 = (body[7] - 33); lng4 = (body[8] - 33); lng1 = ((((lng1 * 91) + lng2) * 91) + lng3) * 91 + lng4; /* calculate latitude and longitude */ lat = 90.0F - ((float)(lat1) / 380926.0F); lng = -180.0F + ((float)(lng1) / 190463.0F); return pbuf_fill_pos(pb, lat, lng, sym_table, sym_code); } /* * Parse an uncompressed "normal" APRS packet * * APRS PROTOCOL REFERENCE 1.0.1 Chapter 8, page 32 (42 in PDF) * */ static int parse_aprs_uncompressed(struct pbuf_t *pb, const char *body, const char *body_end) { char posbuf[20]; unsigned int lat_deg = 0, lat_min = 0, lat_min_frag = 0, lng_deg = 0, lng_min = 0, lng_min_frag = 0; float lat, lng; char lat_hemi, lng_hemi; char sym_table, sym_code; int issouth = 0; int iswest = 0; DEBUG_LOG("parse_aprs_uncompressed"); if (body_end - body < 19) { DEBUG_LOG("\ttoo short"); return 0; } /* make a local copy, so we can overwrite it at will. */ memcpy(posbuf, body, 19); posbuf[19] = 0; // fprintf(stderr, "\tposbuf: %s\n", posbuf); // position ambiquity is going to get ignored now, // it's not needed in this application. /* lat */ if (posbuf[2] == ' ') posbuf[2] = '3'; if (posbuf[3] == ' ') posbuf[3] = '5'; if (posbuf[5] == ' ') posbuf[5] = '5'; if (posbuf[6] == ' ') posbuf[6] = '5'; /* lng */ if (posbuf[12] == ' ') posbuf[12] = '3'; if (posbuf[13] == ' ') posbuf[13] = '5'; if (posbuf[15] == ' ') posbuf[15] = '5'; if (posbuf[16] == ' ') posbuf[16] = '5'; // fprintf(stderr, "\tafter filling amb: %s\n", posbuf); /* 3210.70N/13132.15E# */ if (sscanf(posbuf, "%2u%2u.%2u%c%c%3u%2u.%2u%c%c", &lat_deg, &lat_min, &lat_min_frag, &lat_hemi, &sym_table, &lng_deg, &lng_min, &lng_min_frag, &lng_hemi, &sym_code) != 10) { DEBUG_LOG("\tsscanf failed"); return 0; } if (!valid_sym_table_uncompressed(sym_table)) sym_table = 0; if (lat_hemi == 'S' || lat_hemi == 's') issouth = 1; else if (lat_hemi != 'N' && lat_hemi != 'n') return 0; /* neither north or south? bail out... */ if (lng_hemi == 'W' || lng_hemi == 'w') iswest = 1; else if (lng_hemi != 'E' && lng_hemi != 'e') return 0; /* neither west or east? bail out ... */ if (lat_deg > 89 || lng_deg > 179) return 0; /* too large values for lat/lng degrees */ lat = (float)lat_deg + (float)lat_min / 60.0 + (float)lat_min_frag / 6000.0; lng = (float)lng_deg + (float)lng_min / 60.0 + (float)lng_min_frag / 6000.0; /* Finally apply south/west indicators */ if (issouth) lat = 0.0 - lat; if (iswest) lng = 0.0 - lng; // fprintf(stderr, "\tlat %u %u.%u %c (%.3f) lng %u %u.%u %c (%.3f)\n", // lat_deg, lat_min, lat_min_frag, (int)lat_hemi, lat, // lng_deg, lng_min, lng_min_frag, (int)lng_hemi, lng); // fprintf(stderr, "\tsym '%c' '%c'\n", sym_table, sym_code); return pbuf_fill_pos(pb, lat, lng, sym_table, sym_code); } /* * Parse an APRS object * * APRS PROTOCOL REFERENCE 1.0.1 Chapter 11, page 58 (68 in PDF) * */ static int parse_aprs_object(struct pbuf_t *pb, const char *body, const char *body_end) { int i; int namelen = -1; pb->packettype |= T_OBJECT; DEBUG_LOG("parse_aprs_object"); /* check that the object name ends with either * or _ */ if (*(body + 9) != '*' && *(body + 9) != '_') { DEBUG_LOG("\tinvalid object kill character"); return 0; } /* check that the timestamp ends with one of the valid timestamp type IDs */ char tz_end = body[16]; if (tz_end != 'z' && tz_end != 'h' && tz_end != '/') { DEBUG_LOG("\tinvalid object timestamp character: '%c'", tz_end); return 0; } /* check object's name - scan for non-printable characters and the last * non-space character */ for (i = 0; i < 9; i++) { if (body[i] < 0x20 || body[i] > 0x7e) { DEBUG_LOG("\tobject name has unprintable characters"); return 0; // non-printable } if (body[i] != ' ') namelen = i; } if (namelen < 0) { DEBUG_LOG("\tobject has empty name"); return 0; } pb->srcname = body; pb->srcname_len = namelen+1; DEBUG_LOG("object name: '%.*s'\n", pb->srcname_len, pb->srcname); /* Forward the location parsing onwards */ if (valid_sym_table_compressed(body[17])) return parse_aprs_compressed(pb, body + 17, body_end); if (body[17] >= '0' && body[17] <= '9') return parse_aprs_uncompressed(pb, body + 17, body_end); DEBUG_LOG("no valid position in object"); return 0; } /* * Parse an APRS item * * APRS PROTOCOL REFERENCE 1.0.1 Chapter 11, page 59 (69 in PDF) * */ static int parse_aprs_item(struct pbuf_t *pb, const char *body, const char *body_end) { int i; pb->packettype |= T_ITEM; DEBUG_LOG("parse_aprs_item"); /* check item's name - scan for non-printable characters and the * ending character ! or _ */ for (i = 0; i < 9 && body[i] != '!' && body[i] != '_'; i++) { if (body[i] < 0x20 || body[i] > 0x7e) { DEBUG_LOG("\titem name has unprintable characters"); return 0; /* non-printable */ } } if (body[i] != '!' && body[i] != '_') { DEBUG_LOG("\titem name ends with neither ! or _"); return 0; } if (i < 3 || i > 9) { DEBUG_LOG("\titem name has invalid length"); return 0; } pb->srcname = body; pb->srcname_len = i; //fprintf(stderr, "\titem name: '%.*s'\n", pb->srcname_len, pb->srcname); /* Forward the location parsing onwards */ i++; if (valid_sym_table_compressed(body[i])) return parse_aprs_compressed(pb, body + i, body_end); if (body[i] >= '0' && body[i] <= '9') return parse_aprs_uncompressed(pb, body + i, body_end); DEBUG_LOG("\tno valid position in item"); return 0; } #if 0 int parse_aprs_txgate(struct pbuf_t *pb, int look_inside_3rd_party, historydb_t *historydb) { int rc = parse_aprs(pb, look_inside_3rd_party, historydb); if (pb->packettype & T_THIRDPARTY) { // Tx-IGate needs to know from RF received frames, if there is // source address that arrived from an Tx-IGate... const char *body; const char *body_end; const char *pos_start; const char *info_start = pb->info_start; } return rc; } #endif /* * Try to parse an APRS packet. * Returns 1 if position was parsed successfully, * 0 if parsing failed. * * Does also front-end part of the output filter's * packet type classification job. * * TODO: Recognize TELEM packets in !/=@ packets too! * * Return 0 for parse failures, 1 for OK. */ int parse_aprs(struct pbuf_t*const pb, historydb_t*const historydb) { char packettype, poschar; int paclen; int rc; const char *body; const char *body_end; const char *pos_start; const char *info_start = pb->info_start; int look_inside_3rd_party = 1; // Look there once.. pb->packettype = T_ALL; pb->flags = 0; if (!pb->info_start) return 0; if (pb->data[0] == 'C' && /* Perhaps CWOP ? */ pb->data[1] == 'W') { const char *s = pb->data + 2; const char *pe = pb->data + pb->packet_len; for ( ; *s && s < pe ; ++s ) { int c = *s; if (c < '0' || c > '9') break; } if (*s == '>') pb->packettype |= T_CWOP; } /* the following parsing logic has been translated from Ham::APRS::FAP * Perl module to C */ // ignore the CRLF in the end of the body body_end = pb->data + pb->packet_len; // NOTE! Difference from original aprsc code do { // body is right after the packet type character body = info_start + 1; // length of the info field: paclen = body_end - info_start; if (paclen < 1) return 0; // consumed all, or empty packet // Check the first character of the packet and // determine the packet type packettype = *info_start; // Exit this loop unless it is 3rd-party frame if (packettype != '}') break; // Look for ':' character separating address block // from 3rd-party body info_start = memchr(body, ':', (int)(body_end - body)); if (info_start == NULL) { // Not valid 3rd party frame! return 0; } pb->packettype |= T_THIRDPARTY; if (!look_inside_3rd_party) return 1; // Correct 3rd-party, don't look further. // Look once inside the 3rd party frame, // this is used in aprx's tx-igate, which builds // the 3rd-party frame before parsing message-to-be-tx:ed // .. and doing content filters. --look_inside_3rd_party; pb->packettype = 0; // Skip over the ':' ++info_start; continue; // and loop.. } while (1); switch (packettype) { /* the following are obsolete mic-e types: 0x1c 0x1d * case 0x1c: * case 0x1d: */ case 0x27: /* ' */ case 0x60: /* ` */ /* could be mic-e, minimum body length 9 chars */ if (paclen >= 9) { pb->packettype |= T_POSITION; rc = parse_aprs_mice(pb, (const unsigned char*)body, (const unsigned char*)body_end); DEBUG_LOG("\n"); return rc; } return 0; // bad case '!': if (*body == '!') { /* Ultimeter 2000 - "tnc2addr:!!" */ pb->packettype |= T_WX; return 1; // Known Ultimeter format } case '=': case '/': case '@': /* check that we won't run over right away */ if (body_end - body < 10) return 0; // bad /* Normal or compressed location packet, with or without * timestamp, with or without messaging capability * * ! and / have messaging, / and @ have a prepended timestamp */ pb->packettype |= T_POSITION; if (packettype == '/' || packettype == '@') { /* With a prepended timestamp, jump over it. */ body += 7; } poschar = *body; if (valid_sym_table_compressed(poschar)) { /* [\/\\A-Za-j] */ /* compressed position packet */ rc = 0; if (body_end - body >= 13) rc = parse_aprs_compressed(pb, body, body_end); DEBUG_LOG("\n"); return rc; } else if (poschar >= 0x30 && poschar <= 0x39) { /* [0-9] */ /* normal uncompressed position */ rc = 0; if (body_end - body >= 19) rc = parse_aprs_uncompressed(pb, body, body_end); DEBUG_LOG("\n"); return rc; } return 0; case '$': if (body_end - body > 10) { // Is it OK to declare it as position packet ? rc = parse_aprs_nmea(pb, body, body_end); DEBUG_LOG("\n"); return rc; } return 0; case ':': pb->packettype |= T_MESSAGE; // quick and loose way to identify NWS and SKYWARN messages // they do apparently originate from "WXSRV", but that is not // guaranteed thing... if (memcmp(body,"NWS-",4) == 0) // as seen on specification pb->packettype |= T_NWS; if (memcmp(body,"NWS_",4) == 0) // as seen on data pb->packettype |= T_NWS; if (memcmp(body,"SKY",3) == 0) // as seen on specification pb->packettype |= T_NWS; // Is it perhaps TELEMETRY related "message" ? if ( body[9] == ':' && ( memcmp( body+9, ":PARM.", 6 ) == 0 || memcmp( body+9, ":UNIT.", 6 ) == 0 || memcmp( body+9, ":EQNS.", 6 ) == 0 || memcmp( body+9, ":BITS.", 6 ) == 0 )) { pb->packettype &= ~T_MESSAGE; pb->packettype |= T_TELEMETRY; // Fall through to recipient location lookup } // Or perhaps a DIRECTED QUERY ? if (body[9] == ':' && body[10] == '?') { pb->packettype &= ~T_MESSAGE; pb->packettype |= T_QUERY; // Fall through to recipient location lookup } // Now find out if the message RECIPIENT address is known // to have some location data ? Because then we can treat // them the same way in filters as we do those with real // positions.. { const char *p; int i; #ifndef DISABLE_IGATE history_cell_t *history; #endif pb->dstname = body; p = body; for (i = 0; i < CALLSIGNLEN_MAX; ++i) { // the recipient address is space padded // to 9 chars, while our historydb is not. if (*p == 0 || *p == ' ' || *p == ':') break; } pb->dstname_len = p - body; #ifndef DISABLE_IGATE if (historydb != NULL) { history = historydb_lookup( historydb, pb->dstname, i ); if (history != NULL) { pb->lat = history->lat; pb->lng = history->lon; pb->cos_lat = history->coslat; pb->flags |= F_HASPOS; } } #endif } return 1; case ';': if (body_end - body > 29) { rc = parse_aprs_object(pb, body, body_end); DEBUG_LOG("\n"); return rc; } return 0; // too short case '>': pb->packettype |= T_STATUS; return 1; // ok case '<': pb->packettype |= T_STATCAPA; return 1; // ok case '?': pb->packettype |= T_QUERY; return 1; // ok at igate/digi case ')': if (body_end - body > 18) { rc = parse_aprs_item(pb, body, body_end); DEBUG_LOG("\n"); return rc; } return 0; // too short case 'T': if (body_end - body > 18) { pb->packettype |= T_TELEMETRY; rc = parse_aprs_telem(pb, body, body_end); DEBUG_LOG("\n"); return rc; } return 0; // too short case '#': /* Peet Bros U-II Weather Station */ case '*': /* Peet Bros U-I Weather Station */ case '_': /* Weather report without position */ pb->packettype |= T_WX; return 1; // good case '{': pb->packettype |= T_USERDEF; return 1; // okay at digi? // the packettype is never '}' // case '}': // pb->packettype |= T_THIRDPARTY; // return 1; // 3rd-party is okay at digi default: break; } /* When all else fails, try to look for a !-position that can * occur anywhere within the 40 first characters according * to the spec. (X1J TNC digipeater bugs...) */ pos_start = memchr(body, '!', body_end - body); if ((pos_start) && pos_start - body <= 39) { poschar = *pos_start; if (valid_sym_table_compressed(poschar)) { /* [\/\\A-Za-j] */ /* compressed position packet */ int rc = 0; if (body_end - pos_start >= 13) rc = parse_aprs_compressed(pb, pos_start, body_end); DEBUG_LOG("\n"); return rc; } else if (poschar >= 0x30 && poschar <= 0x39) { /* [0-9] */ /* normal uncompressed position */ int rc = 0; if (body_end - pos_start >= 19) rc = parse_aprs_uncompressed(pb, pos_start, body_end); DEBUG_LOG("\n"); return rc; } } return 0; // bad } /* * Parse an aprs text message (optional, only done to messages addressed to * SERVER */ int parse_aprs_message(const struct pbuf_t *pb, struct aprs_message_t * const am) { const char *p; memset(am, 0, sizeof(*am)); if (!(pb->packettype & T_MESSAGE)) return -1; if (pb->info_start[10] != ':') return -2; am->body = pb->info_start + 11; /* -2 for the CRLF already in place */ am->body_len = pb->packet_len - 2 - (pb->info_start - pb->data); /* search for { looking backwards from the end of the packet, * it separates the msgid */ p = am->body + am->body_len - 1; while (p > am->body && *p != '{') p--; if (*p == '{') { am->msgid = p+1; am->msgid_len = pb->packet_len - 2 - (am->msgid - pb->data); am->body_len = p - am->body; } /* check if this is an ACK */ if ((!am->msgid_len) && am->body_len > 3 && am->body[0] == 'a' && am->body[1] == 'c' && am->body[2] == 'k') { am->is_ack = 1; am->msgid = am->body + 3; am->msgid_len = am->body_len - 3; am->body_len = 0; return 0; } /* check if this is an REJ */ if ((!am->msgid_len) && am->body_len > 3 && am->body[0] == 'r' && am->body[1] == 'e' && am->body[2] == 'j') { am->is_rej = 1; am->msgid = am->body + 3; am->msgid_len = am->body_len - 3; am->body_len = 0; return 0; } return 0; } aprx-2.08.svn593/aprsis.c0000644000175000017500000007230112313325035014122 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ /* This code works only with single aprsis-server instance! */ #include "aprx.h" #ifndef DISABLE_IGATE #include #include #include #include #ifdef HAVE_NETINET_SCTP_H #include #endif #if defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD) #include pthread_t aprsis_thread; pthread_attr_t pthr_attrs; #endif /* * $aprsserver = "rotate.aprs.net:14580"; * * re-resolve the $aprsserver at each connection setup! * * The APRS-IS system connection runs as separate sub-process, once it starts. * This way the main-loop is independent from uncertainties of DNS resolving * delay times in this part of the code. * */ enum aprsis_mode { MODE_TCP, MODE_SSL, MODE_SCTP, MODE_DTLS }; static char default_passcode[] = "-1"; struct aprsis_host { char *server_name; char *server_port; char *login; char *pass; char *filterparam; int heartbeat_monitor_timeout; enum aprsis_mode mode; }; struct aprsis { int server_socket; struct aprsis_host *H; time_t next_reconnect; time_t last_read; int wrbuf_len; int wrbuf_cur; int rdbuf_len; int rdbuf_cur; int rdlin_len; char wrbuf[16000]; char rdbuf[3000]; char rdline[500]; }; char * const aprsis_loginid; static struct aprsis *AprsIS; static struct aprsis_host **AISh; static int AIShcount; static int AIShindex; static int aprsis_up = -1; /* up & down talking socket(pair), The aprsis talker (thread/child) uses this socket. */ static int aprsis_down = -1; /* down talking socket(pair), The aprx main loop uses this socket */ //static dupecheck_t *aprsis_rx_dupecheck; //int aprsis_dupecheck_storetime = 30; extern int log_aprsis; extern int die_now; void aprsis_init(void) { aprsis_up = -1; aprsis_down = -1; } //void enable_aprsis_rx_dupecheck(void) { // aprsis_rx_dupecheck = dupecheck_new(aprsis_dupecheck_storetime); //} #if !(defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD)) static void sig_handler(int sig) { die_now = 1; signal(sig, sig_handler); } #endif /* *Close APRS-IS server_socket, clean state.. */ // APRS-IS communicator static void aprsis_close(struct aprsis *A, const char *why) { if (A->server_socket >= 0) close(A->server_socket); /* close, and flush write buffers */ A->server_socket = -1; A->wrbuf_len = A->wrbuf_cur = 0; A->next_reconnect = tick.tv_sec + 10; A->last_read = tick.tv_sec; if (!A->H) return; /* Not connected, nor defined.. */ aprxlog("CLOSE APRSIS %s:%s %s", A->H->server_name, A->H->server_port, why ? why : ""); } /* * aprsis_queue_() - internal routine - queue data to specific APRS-IS instance */ // APRS-IS communicator static int aprsis_queue_(struct aprsis *A, const char * const addr, const char qtype, const char *gwcall, const char * const text, int textlen) { int i; char addrbuf[1000]; int addrlen, len; char * p; /* Queue for sending to APRS-IS only when the socket is operational */ if (A->server_socket < 0) return 1; /* Here the A->H->login is always set. */ /* * Append stuff on the writebuf, if it fits. * If it does not fit, something is broken already * and we just drop it.. * * Just to make sure that the write pointer is not left * rewound when all has been done... */ if (A->wrbuf_cur >= A->wrbuf_len && A->wrbuf_len > 0) A->wrbuf_cur = A->wrbuf_len = 0; addrlen = 0; if (addr) addrlen = sprintf(addrbuf, "%s,qA%c,%s:", addr, qtype, (gwcall && *gwcall) ? gwcall : A->H->login); aprsis_login = A->H->login; len = addrlen + textlen; /* Does it fit in ? */ if ((sizeof(A->wrbuf) - 10) <= (A->wrbuf_len + len)) { /* The string does not fit in, perhaps it needs compacting ? */ if (A->wrbuf_cur > 0) { /* Compacting is possible ! */ memcpy(A->wrbuf, A->wrbuf + A->wrbuf_cur, A->wrbuf_len - A->wrbuf_cur); A->wrbuf_len -= A->wrbuf_cur; A->wrbuf_cur = 0; } /* Check again if it fits in.. */ if ((sizeof(A->wrbuf) - 10) <= (A->wrbuf_len + len)) { /* NOT! Too bad, drop it.. */ return 2; } } /* Place it on our send buffer */ if (addrlen > 0) { memcpy(A->wrbuf + A->wrbuf_len, addrbuf, addrlen); A->wrbuf_len += addrlen; } /* If there is CR or LF within the packet, terminate packet at it.. */ p = memchr(text, '\r', textlen); if (p != NULL) { textlen = p - text; } p = memchr(text, '\n', textlen); if (p != NULL) { textlen = p - text; } /* Append CR+LF at the end of the packet */ p = (char*)(text + textlen); *p++ = '\r'; *p++ = '\n'; textlen += 2; memcpy(A->wrbuf + A->wrbuf_len, text, textlen); A->wrbuf_len += textlen; /* Always supplied with tail newline.. */ /* -- debug -- fwrite(A->wrbuf,A->wrbuf_len,1,stdout); return 0; */ /* Try writing it right away: */ i = write(A->server_socket, A->wrbuf + A->wrbuf_cur, A->wrbuf_len - A->wrbuf_cur); if (i > 0) { // the buffer's last character is \n, don't write it if (log_aprsis) aprxlog(A->wrbuf + A->wrbuf_cur, (A->wrbuf_len - A->wrbuf_cur) -1, "<< %s:%s << ", A->H->server_name, A->H->server_port); A->wrbuf_cur += i; if (A->wrbuf_cur >= A->wrbuf_len) { /* Wrote all ! */ A->wrbuf_cur = A->wrbuf_len = 0; } } return 0; } /* * THIS CONNECT ROUTINE WILL BLOCK (At DNS resolving) * * This is why APRSIS communication is run at either * a fork()ed child, or separate pthread from main loop. */ // APRS-IS communicator static void aprsis_reconnect(struct aprsis *A) { struct addrinfo req, *ai, *a, *ap[21]; int i, n; char *s; char aprsislogincmd[3000]; const char *errstr; int errcode; memset(aprsislogincmd, 0, sizeof(aprsislogincmd)); // please valgrind aprsis_close(A, "reconnect"); if (!A->H) { A->H = AISh[0]; } else { ++AIShindex; if (AIShindex >= AIShcount) AIShindex = 0; A->H = AISh[AIShindex]; } if (!A->H->login) { if (log_aprsis) aprxlog("FAIL - APRSIS-LOGIN not defined, no APRSIS connection!"); return; /* Will try to reconnect in about 60 seconds.. */ } aprsis_login = A->H->login; memset(&req, 0, sizeof(req)); req.ai_socktype = SOCK_STREAM; req.ai_protocol = IPPROTO_TCP; req.ai_flags = 0; #if 1 req.ai_family = AF_UNSPEC; /* IPv4 and IPv6 are both OK */ #else req.ai_family = AF_INET; /* IPv4 only */ #endif ai = NULL; i = getaddrinfo(A->H->server_name, A->H->server_port, &req, &ai); errstr = "address resolution failure"; errcode = errno; if (i != 0) { fail_out:; /* Discard stuff and redo latter.. */ if (ai) freeaddrinfo(ai); aprsis_close(A, "fail on connect"); aprxlog("FAIL - Connect to %s:%s failed: %s - errno=%d - %s", A->H->server_name, A->H->server_port, errstr, errno, strerror(errcode)); return; } /* Count the addresses */ memset(ap, 0, sizeof(ap)); for (n = 0, a = ai; a; a = a->ai_next, ++n) { if (n < 20) ap[n] = a; else break; } ap[n] = NULL; if (n > 1) { /* more than one ? choose one at random as the first address, then go through the address list in new sequence. */ n = rand() % n; if (n > 0) { a = ap[n]; ap[n] = ap[0]; ap[0] = a; } } for (n = 0; (a = ap[n]) && A->server_socket < 0; ++n) { errstr = "socket formation failed"; A->server_socket = socket(a->ai_family, a->ai_socktype, a->ai_protocol); errcode = errno; if (A->server_socket < 0) continue; errstr = "connection failed"; i = connect(A->server_socket, a->ai_addr, a->ai_addrlen); errcode = errno; if (i < 0) { /* If connection fails, try next possible address */ close(A->server_socket); A->server_socket = -1; continue; } } if (A->server_socket < 0) goto fail_out; freeaddrinfo(ai); ai = NULL; timetick(); // unpredictable time since system did last poll.. if (time_reset) { if (debug) printf("In time_reset mode, no touching yet!\n"); A->next_reconnect = tick.tv_sec + 10; return; } aprxlog("CONNECT APRSIS %s:%s", A->H->server_name, A->H->server_port); /* From now the socket will be non-blocking for its entire lifetime.. */ fd_nonblockingmode(A->server_socket); /* We do at first sync writing of login, and such.. */ s = aprsislogincmd; s += sprintf(s, "user %s pass %s vers %s %s", A->H->login, A->H->pass, swname, swversion); if (A->H->filterparam) s += sprintf(s, " filter %s", A->H->filterparam); A->last_read = tick.tv_sec; aprsis_queue_(A, NULL, qTYPE_LOCALGEN, "", aprsislogincmd, strlen(aprsislogincmd)); return; /* just a place-holder */ } // APRS-IS communicator static int aprsis_sockreadline(struct aprsis *A) { int i, c; /* Reads multiple lines from buffer, Last one is left into incomplete state */ for (i = A->rdbuf_cur; i < A->rdbuf_len; ++i) { c = 0xFF & (A->rdbuf[i]); if (c == '\r' || c == '\n') { /* End of line, process.. */ if (A->rdlin_len > 0) { A->rdline[A->rdlin_len] = 0; /* */ A->last_read = tick.tv_sec; /* Time stamp me ! */ if (log_aprsis) aprxlog(A->rdline, A->rdlin_len, ">> %s:%s >> ", A->H->server_name, A->H->server_port); /* Send the A->rdline content to main program */ c = send(aprsis_up, A->rdline, A->rdlin_len, 0); /* This may fail with SIGPIPE.. */ if (c < 0 && (errno == EPIPE || errno == ECONNRESET || errno == ECONNREFUSED || errno == ENOTCONN)) { die_now = 1; // upstream socket send failed } } A->rdlin_len = 0; continue; } if (A->rdlin_len < sizeof(A->rdline) - 2) { A->rdline[A->rdlin_len++] = c; } } A->rdbuf_cur = 0; A->rdbuf_len = 0; /* we ignore line reading */ return 0; /* .. this is placeholder.. */ } // APRS-IS communicator static int aprsis_sockread(struct aprsis *A) { int i; int rdspace = sizeof(A->rdbuf) - A->rdbuf_len; if (A->rdbuf_cur > 0) { /* Read-out cursor is not at block beginning, is there unread data too ? */ if (A->rdbuf_cur > A->rdbuf_len) { memcpy(A->rdbuf, A->rdbuf + A->rdbuf_cur, A->rdbuf_len - A->rdbuf_cur); A->rdbuf_len -= A->rdbuf_cur; } else A->rdbuf_len = 0; /* all processed, mark its size zero */ A->rdbuf_cur = 0; /* recalculate */ rdspace = sizeof(A->rdbuf) - A->rdbuf_len; } i = read(A->server_socket, A->rdbuf + A->rdbuf_len, rdspace); if (i > 0) { A->rdbuf_len += i; /* we just ignore the readback.. but do time-stamp the event */ A->last_read = tick.tv_sec; aprsis_sockreadline(A); } return i; } struct aprsis_tx_msg_head { time_t then; int addrlen; int gwlen; int textlen; char qtype; }; /* * Read frame from a socket in between main-program and * APRS-IS interface subprogram. (At APRS-IS side.) * */ // APRS-IS communicator static void aprsis_readup(void) { int i; char buf[10000]; const char *addr; const char *gwcall; const char *text; int textlen; struct aprsis_tx_msg_head head; i = recv(aprsis_up, buf, sizeof(buf), 0); if (i == 0) { /* EOF ! */ if (debug>1) printf("Upstream fd read resulted eof status.\n"); die_now = 1; return; } if (i < 0) { return; /* Whatever was the reason.. */ } buf[i] = 0; /* String Termination NUL byte */ memcpy(&head, buf, sizeof(head)); if (head.then + 10 < tick.tv_sec) return; /* Too old, discard */ addr = buf + sizeof(head); gwcall = addr + head.addrlen + 1; text = gwcall + head.gwlen + 1; textlen = head.textlen; if (textlen <= 2) return; // BAD! if ((text + textlen) > (buf + i)) { return; // BAD! } /* printf("addrlen=%d addr=%s\n",head.addrlen, addr); printf("gwlen=%d gwcall=%s\n",head.gwlen,gwcall); printf("textlen=%d text=%s",head.textlen, text); return; */ /* Now queue the thing! */ if (AprsIS != NULL) aprsis_queue_(AprsIS, addr, head.qtype, gwcall, text, textlen); } // main program side int aprsis_queue(const char *addr, int addrlen, const char qtype, const char *gwcall, const char *text, int textlen) { static char *buf; /* Dynamically allocated buffer... */ static int buflen; int i, len, gwlen = strlen(gwcall); char *p; struct aprsis_tx_msg_head head; int newlen; // dupe_record_t *dp; if (aprsis_down < 0) return -1; // No socket! if (addrlen == 0) /* should never be... */ addrlen = strlen(addr); // if (aprsis_rx_dupecheck != NULL) { // dp = dupecheck_aprs( aprsis_rx_dupecheck, // addr, addrlen, // text, textlen ); // if (dp != NULL) return 1; // Bad either as dupe, or due to alloc failure // } newlen = sizeof(head) + addrlen + gwlen + textlen + 6; if (newlen > buflen) { buflen = newlen; buf = realloc(buf, buflen); memset(buf, 0, buflen); // (re)init it to silence valgrind } memset(&head, 0, sizeof(head)); head.then = tick.tv_sec; head.addrlen = addrlen; head.gwlen = gwlen; head.textlen = textlen; head.qtype = qtype; memcpy(buf, &head, sizeof(head)); p = buf + sizeof(head); memcpy(p, addr, addrlen); p += addrlen; *p++ = 0; /* string terminating 0 byte */ memcpy(p, gwcall, gwlen); p += gwlen; *p++ = 0; /* string terminating 0 byte */ memcpy(p, text, textlen); p += textlen; len = p - buf; *p++ = 0; #ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0 /* This exists only on Linux */ #endif i = send(aprsis_down, buf, len, MSG_NOSIGNAL); /* No SIGPIPE if the receiver is out, or pipe is full because it is doing slow reconnection. */ return (i != len); /* Return 0 if ANY of the queue operations was successfull Return 1 if there was some error.. */ } // APRS-IS communicator static int aprsis_prepoll_(struct aprxpolls *app) { struct pollfd *pfd; struct aprsis *A = AprsIS; if (A->last_read == 0) A->last_read = tick.tv_sec; /* mark it non-zero.. */ if (A->server_socket < 0) return -1; /* Not open, do nothing */ if (debug>3) printf("aprsis_prepoll_()\n"); if (time_reset) { aprsis_close(A, "time_reset!"); } /* Not all aprs-is systems send "heartbeat", but when they do.. */ if ((A->H->heartbeat_monitor_timeout > 0) && ((A->last_read + A->H->heartbeat_monitor_timeout - tick.tv_sec) < 0)) { /* * More than 120 seconds (2 minutes) since last time * that APRS-IS systems told us something on the connection. * There is a heart-beat ticking every 20 or so seconds. */ aprsis_close(A, "heartbeat timeout"); } /* FD is open, lets mark it for poll read.. */ pfd = aprxpolls_new(app); pfd->fd = A->server_socket; pfd->events = POLLIN | POLLPRI | POLLERR | POLLHUP; pfd->revents = 0; /* Do we have something for writing ? */ if (A->wrbuf_len) { pfd->events |= POLLOUT; } return 0; } // APRS-IS communicator static int aprsis_postpoll_(struct aprxpolls *app) { int i; struct pollfd *pfd = app->polls; struct aprsis *A = AprsIS; if (debug>3) printf("aprsis_postpoll_() cnt=%d\n", app->pollcount); for (i = 0; i < app->pollcount; ++i, ++pfd) { if (pfd->fd == A->server_socket && pfd->fd >= 0) { /* This is APRS-IS socket, and we may have some results.. */ if (pfd->revents & (POLLERR)) { /* Errors ? */ aprsis_close(A,"postpoll_ POLLERR"); continue; } if (pfd->revents & (POLLHUP)) { /* Errors ? */ aprsis_close(A,"postpoll_ POLLHUP"); continue; } if (pfd->revents & (POLLIN | POLLPRI)) { /* Ready for reading */ for (;;) { i = aprsis_sockread(A); if (i == 0) { /* EOF ! */ aprsis_close(A,"postpoll_ EOF"); continue; } if (i < 0) break; } } if (pfd->revents & POLLOUT) { /* Ready for writing */ /* Normal queue write processing */ if (A->wrbuf_len > 0 && A->wrbuf_cur < A->wrbuf_len) { i = write(A->server_socket, A->wrbuf + A->wrbuf_cur, A->wrbuf_len - A->wrbuf_cur); if (debug>2) printf("%ld << %s:%s << write() rc= %d\n", tick.tv_sec, A->H->server_name, A->H->server_port, i); if (i < 0) continue; /* Argh.. nothing */ // if (i == 0); /* What ? */ if (log_aprsis) aprxlog(A->wrbuf + A->wrbuf_cur, (A->wrbuf_len - A->wrbuf_cur) -1, "<< %s:%s << ", A->H->server_name, A->H->server_port); A->wrbuf_cur += i; if (A->wrbuf_cur >= A->wrbuf_len) { /* Wrote all! */ A->wrbuf_len = A->wrbuf_cur = 0; } else { /* partial write .. do nothing.. */ } } /* .. normal queue */ } /* .. POLLOUT */ } /* .. if fd == server_socket */ } /* .. for .. nfds .. */ return 1; /* there was something we did, maybe.. */ } // APRS-IS communicator static void aprsis_cond_reconnect(void) { if (AprsIS && /* First time around it may trip.. */ AprsIS->server_socket < 0 && (AprsIS->next_reconnect - tick.tv_sec) <= 0) { aprsis_reconnect(AprsIS); } } /* * Main-loop of subprogram handling communication with * APRS-IS network servers. * * This starts only when we have at least one defined without errors. */ // APRS-IS communicator static void aprsis_main(void) { #if !(defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD)) int ppid = getppid(); #endif struct aprxpolls app = APRXPOLLS_INIT; #if !(defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD)) signal(SIGHUP, sig_handler); signal(SIGPIPE, SIG_IGN); #endif /* The main loop */ while (!die_now) { struct pollfd *pfd; int i; timetick(); aprsis_cond_reconnect(); // may take unpredictable time.. timetick(); #if !(defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD)) // Parent-pid makes no sense in threaded setup i = getppid(); if (i != ppid) break; /* die now, my parent is gone.. */ if (i == 1) break; /* a safety fallback case.. */ #endif aprxpolls_reset(&app); tv_timeradd_seconds( &app.next_timeout, &tick, 5 ); if (aprsis_up >= 0) { pfd = aprxpolls_new(&app); pfd->fd = aprsis_up; pfd->events = POLLIN | POLLPRI | POLLERR | POLLHUP; pfd->revents = 0; } i = aprsis_prepoll_(&app); // Prepolls are done time_reset = 0; if (tv_timercmp(&app.next_timeout, &tick) <= 0) { tv_timeradd_seconds( &app.next_timeout, &tick, 1 ); // Just to be on safe side.. } i = poll(app.polls, app.pollcount, aprxpolls_millis(&app)); timetick(); assert(app.polls != NULL); if (app.polls[0]. revents & (POLLIN | POLLPRI | POLLERR | POLLHUP)) { /* messaging channel has something for us, if the channel reports EOF, we exit there and then. */ aprsis_readup(); } i = aprsis_postpoll_(&app); } aprxpolls_free(&app); // valgrind.. /* Got "DIE NOW" signal... */ // exit(0); } /* * aprsis_add_server() - old style configuration */ int aprsis_add_server(const char *server, const char *port) { struct aprsis_host *H; if (AprsIS == NULL) { AprsIS = calloc(1,sizeof(*AprsIS)); } H = calloc(1,sizeof(*H)); AISh = realloc(AISh, sizeof(AISh[0]) * (AIShcount + 1)); AISh[AIShcount] = H; ++AIShcount; /* No inc on AprsIScount */ H->server_name = strdup(server); H->server_port = strdup(port); H->heartbeat_monitor_timeout = 120; // Default timeout 120 seconds H->login = strdup(aprsis_login); // global aprsis_login H->pass = default_passcode; if (H->login == NULL) H->login = strdup(mycall); AprsIS->server_socket = -1; AprsIS->next_reconnect = tick.tv_sec +10; /* perhaps somewhen latter.. */ return 0; } // old style configuration int aprsis_set_heartbeat_timeout(const int tout) { int i = AIShcount; struct aprsis_host *H; if (i > 0) --i; H = AISh[i]; H->heartbeat_monitor_timeout = tout; return 0; } // old style configuration int aprsis_set_filter(const char *filter) { int i = AIShcount; struct aprsis_host *H; if (i > 0) --i; H = AISh[i]; H->filterparam = strdup(filter); return 0; } // old style configuration int aprsis_set_login(const char *login) { int i = AIShcount; struct aprsis_host *H; if (i > 0) --i; H = AISh[i]; H->login = strdup(login); return 0; } #if defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD) static void aprsis_runthread(void) { sigset_t sigs_to_block; sigemptyset(&sigs_to_block); sigaddset(&sigs_to_block, SIGALRM); sigaddset(&sigs_to_block, SIGINT); sigaddset(&sigs_to_block, SIGTERM); sigaddset(&sigs_to_block, SIGQUIT); sigaddset(&sigs_to_block, SIGHUP); sigaddset(&sigs_to_block, SIGURG); sigaddset(&sigs_to_block, SIGPIPE); sigaddset(&sigs_to_block, SIGUSR1); pthread_sigmask(SIG_BLOCK, &sigs_to_block, NULL); // generally the cancelability is enabled pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); if (debug) printf("aprsis_runthread()\n"); aprsis_main(); } void aprsis_start(void) { int i; int pipes[2]; if (AISh == NULL || AprsIS == NULL) { fprintf(stderr,"***** NO APRSIS SERVER CONNECTION DEFINED *****"); return; } i = socketpair(AF_UNIX, SOCK_DGRAM, PF_UNSPEC, pipes); if (i != 0) { return; /* FAIL ! */ } fd_nonblockingmode(pipes[0]); fd_nonblockingmode(pipes[1]); aprsis_down = pipes[0]; aprsis_up = pipes[1]; if (debug)printf("aprsis_start() PTHREAD socketpair(up=%d,down=%d)\n", aprsis_up, aprsis_down); pthread_attr_init(&pthr_attrs); /* 64 kB stack is enough for this thread (I hope!) default of 2 MB is way too much...*/ pthread_attr_setstacksize(&pthr_attrs, 64*1024); i = pthread_create(&aprsis_thread, &pthr_attrs, (void*)aprsis_runthread, NULL); if (i == 0) { if (debug) printf("APRSIS pthread_create() OK!\n"); } else { // FAIL! close(pipes[0]); close(pipes[1]); aprsis_down = -1; aprsis_up = -1; } } // Shutdown the aprsis thread void aprsis_stop(void) { die_now = 1; pthread_cancel(aprsis_thread); pthread_join(aprsis_thread, NULL); } #else // No pthread(3p) void aprsis_start(void) { int i; int pipes[2]; if (AISh == NULL || AprsIS == NULL) { fprintf(stderr,"***** NO APRSIS SERVER CONNECTION DEFINED *****"); return; } i = socketpair(AF_UNIX, SOCK_DGRAM, PF_UNSPEC, pipes); if (i != 0) { return; /* FAIL ! */ } i = fork(); if (i < 0) { close(pipes[0]); close(pipes[1]); return; /* FAIL ! */ } if (i == 0) { /* Child -- the APRSIS talker */ aprsis_up = pipes[1]; fd_nonblockingmode(pipes[1]); close(pipes[0]); aprsis_main(); exit(0); } /* Parent */ close(pipes[1]); fd_nonblockingmode(pipes[0]); aprsis_down = pipes[0]; } void aprsis_stop(void) { } #endif /* * main-program side pre-poll */ int aprsis_prepoll(struct aprxpolls *app) { int idx = 0; /* returns number of *fds filled.. */ struct pollfd *pfd; // if (debug>3) printf("aprsis_prepoll()\n"); pfd = aprxpolls_new(app); pfd->fd = aprsis_down; /* APRS-IS communicator server Sub-process */ pfd->events = POLLIN | POLLPRI; pfd->revents = 0; /* We react only for reading, if write fails because the socket is jammed, that is just too bad... */ ++idx; return idx; } /* * main-program side reading of aprsis_down */ static int aprsis_comssockread(int fd) { int i; char buf[10000]; i = recv(fd, buf, sizeof(buf), 0); if (debug>3) printf("aprsis_comsockread(fd=%d) -> i = %d\n", fd, i); if (i == 0) return 0; /* TODO: do something with the data ? A receive-only iGate does nothing, but Rx/Tx would do... */ /* Send the frame to Tx-IGate function */ if (i > 0) igate_from_aprsis(buf, i); return 1; } /* * main-program side post-poll */ int aprsis_postpoll(struct aprxpolls *app) { int i; struct pollfd *pfd = app->polls; // if (debug>3) printf("aprsis_postpoll()\n"); for (i = 0; i < app->pollcount; ++i, ++pfd) { if (pfd->fd == aprsis_down) { /* This is APRS-IS communicator subprocess socket, and we may have some results.. */ if (pfd->revents) { /* Ready for reading */ i = aprsis_comssockread(pfd->fd); if (i == 0) { /* EOF ! */ printf("APRS-IS coms subprocess socket EOF from main program side!\n"); continue; } if (i < 0) continue; } } } /* .. for .. nfds .. */ return 1; /* there was something we did, maybe.. */ } // main program side int aprsis_config(struct configfile *cf) { char *name, *param1; char *str = cf->buf; int has_fault = 0; int line0 = cf->linenum; struct aprsis_host *AIH = calloc(1,sizeof(*AIH)); AIH->login = strdup(mycall); AIH->pass = default_passcode; AIH->heartbeat_monitor_timeout = 120; AIH->mode = MODE_TCP; // default mode while (readconfigline(cf) != NULL) { if (configline_is_comment(cf)) continue; /* Comment line, or empty line */ // It can be severely indented... str = config_SKIPSPACE(cf->buf); name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRLOWER(name); param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (strcmp(name, "") == 0) { // End of this interface definition block break; } // APRSIS parameters // login // server // filter // heartbeat-timeout // mode if (strcmp(name, "login") == 0) { if (strcasecmp("$mycall",param1) != 0) { // If not "$mycall" .. config_STRUPPER(param1); if (!validate_callsign_input(param1,0)) { // bad input... } if (debug) printf("%s:%d: INFO: LOGIN = '%s' '%s'\n", cf->name, cf->linenum, param1, str); if (AIH->login) free(AIH->login); AIH->login = strdup(param1); } } else if (strcmp(name, "passcode") == 0) { if (debug) printf("%s:%d: INFO: PASSCODE = '%s' '%s'\n", cf->name, cf->linenum, param1, str); AIH->pass = strdup(param1); } else if (strcmp(name, "server") == 0) { if (AIH->server_name) free(AIH->server_name); AIH->server_name = strdup(param1); param1 = str; str = config_SKIPTEXT(str, NULL); // coverity[returned_pointer] str = config_SKIPSPACE(str); if ('1' <= *param1 && *param1 <= '9') { // fixme: more input analysis? int port = atoi(param1); if (port < 1 || port > 65535) { printf("%s:%d INFO: SERVER = '%s' port='%s' is not supplying valid TCP port number, defaulting to '14580'\n", cf->name, cf->linenum, AIH->server_name, param1); param1 = "14580"; } AIH->server_port = strdup(param1); } else if (*param1 == 0) { // Default silently! AIH->server_port = strdup("14580"); } else { AIH->server_port = strdup("14580"); printf("%s:%d INFO: SERVER = '%s' port='%s' is not supplying valid TCP port number, defaulting to '14580'\n", cf->name, cf->linenum, AIH->server_name, param1); } if (debug) printf("%s:%d: INFO: SERVER = '%s':'%s'\n", cf->name, cf->linenum, AIH->server_name, AIH->server_port); } else if (strcmp(name, "heartbeat-timeout") == 0) { int i = 0; if (config_parse_interval(param1, &i)) { // FIXME: Report parameter failure ... printf("%s:%d: ERROR: HEARTBEAT-TIMEOUT = '%s' - bad parameter'\n", cf->name, cf->linenum, param1); has_fault = 1; } if (i < 0) { /* param failure ? */ i = 0; /* no timeout */ printf("%s:%d: ERROR: HEARTBEAT-TIMEOUT = '%s' - bad parameter'\n", cf->name, cf->linenum, param1); has_fault = 1; } AIH->heartbeat_monitor_timeout = i; if (debug) printf("%s:%d: INFO: HEARTBEAT-TIMEOUT = '%d' '%s'\n", cf->name, cf->linenum, i, str); } else if (strcmp(name, "filter") == 0) { int l1 = (AIH->filterparam != NULL) ? strlen(AIH->filterparam) : 0; int l2 = strlen(param1); AIH->filterparam = realloc( AIH->filterparam, l1 + l2 +2 ); if (l1 > 0) { AIH->filterparam[l1] = ' '; memcpy(&(AIH->filterparam[l1+1]), param1, l2+1); } else { memcpy(&(AIH->filterparam[0]), param1, l2+1); } if (debug) printf("%s:%d: INFO: FILTER = '%s' --> '%s'\n", cf->name, cf->linenum, param1, AIH->filterparam); } else if (strcmp(name, "mode") == 0) { if (strcmp(param1,"tcp") == 0) { AIH->mode = MODE_TCP; } else if (strcmp(param1,"ssl") == 0) { AIH->mode = MODE_SSL; } else if (strcmp(param1,"sctp") == 0) { AIH->mode = MODE_SCTP; } else if (strcmp(param1,"dtls") == 0) { AIH->mode = MODE_DTLS; } else { printf("%s:%d: ERROR: Unknown mode keyword in block: '%s'\n", cf->name, cf->linenum, param1); has_fault = 1; } } else { printf("%s:%d: ERROR: Unknown configuration keyword in block: '%s'\n", cf->name, cf->linenum, name); has_fault = 1; } } if (AIH->server_name == NULL) { printf("%s:%d ERROR: This block does not define server!\n", cf->name, line0); has_fault = 1; } if (has_fault) { if (AIH->server_name != NULL) free(AIH->server_name); if (AIH->server_port != NULL) free(AIH->server_port); if (AIH->filterparam != NULL) free(AIH->filterparam); if (AIH->login != NULL) free(AIH->login); free(AIH); } else { if (AprsIS == NULL) { AprsIS = calloc(1, sizeof(*AprsIS)); AprsIS->server_socket = -1; AprsIS->next_reconnect = tick.tv_sec +10; } if (AIH->pass == default_passcode) { printf("%s:%d WARNING: This block does not define passcode!\n", cf->name, line0); printf("%s:%d WARNING: Your beacons and RF received will not make it to APRS-IS.\n", cf->name, line0); } AISh = realloc(AISh, sizeof(AISh[0]) * (AIShcount + 1)); AISh[AIShcount] = AIH; } return has_fault; } #endif aprx-2.08.svn593/ttyreader.c0000644000175000017500000007115512320105344014627 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #define _SVID_SOURCE 1 #include "aprx.h" #include #include #include /* The ttyreader does read TTY ports into a big buffer, and then from there to packet frames depending on what is attached... */ static struct serialport **ttys; static int ttycount; /* How many are defined ? */ #define TTY_OPEN_RETRY_DELAY_SECS 30 static int poll_millis; /* milliseconds (0 = none.) */ static struct timeval poll_millis_tv; void hexdumpfp(FILE *fp, const uint8_t *buf, const int len, int axaddr) { int i, j; for (i = 0, j=1; i < len; ++i,++j) { int c = buf[i] & 0xFF; fprintf(fp, "%02x", c); if (j < 8) fputc(' ',fp); else { fputc('|',fp); j = 0; } } fprintf(fp, " = "); for (i = 0, j = 1; i < len; ++i,++j) { int c = buf[i] & 0xFF; /* if ((c & 0x81) == 0x80 && (i < 8)) { // Auto-trigger AX.25 address plaintext converting axaddr = 1; } */ if (axaddr && ((c & 0x01) == 1) && i > 3) { // Definitely not AX.25 address anymore.. axaddr = 0; } if (axaddr) { // Shifted AX.25 address byte? c >>= 1; } if (c < 0x20 || c > 0x7E) c = '.'; fputc(c, fp); if (j >= 8) { fputc('|',fp); j = 0; } } } /* * ttyreader_getc() -- pick one char ( >= 0 ) out of input buffer, or -1 if out of buffer */ int ttyreader_getc(struct serialport *S) { if (S->rdcursor >= S->rdlen) { /* Out of data ? */ if (S->rdcursor) S->rdcursor = S->rdlen = 0; /* printf("-\n"); */ return -1; } /* printf(" %02X", 0xFF & S->rdbuf[S->rdcursor++]); */ return (0xFF & S->rdbuf[S->rdcursor++]); } /* * ttyreader_pulltnc2() -- process a line of text by calling * TNC2 UI Monitor analyzer */ static int ttyreader_pulltnc2(struct serialport *S) { const uint8_t *p; int addrlen = 0; p = memchr(S->rdline, ':', S->rdlinelen); if (p != NULL) addrlen = (int)(p - S->rdline); erlang_add(S->ttycallsign[0], ERLANG_RX, S->rdlinelen, 1); /* Account one packet */ /* Send the frame to internal AX.25 network */ /* netax25_sendax25_tnc2(S->rdline, S->rdlinelen); */ #ifndef DISABLE_IGATE /* S->rdline[] has text line without line ending CR/LF chars */ igate_to_aprsis(S->ttycallsign[0], 0, (char *) (S->rdline), addrlen, S->rdlinelen, 0, 1); #endif return 0; } #if 0 /* * ttyreader_pullaea() -- process a line of text by calling * AEA MONITOR 1 analyzer */ static int ttyreader_pullaea(struct serialport *S) { int i; if (S->rdline[S->rdlinelen - 1] == ':') { /* Could this be the AX25 header ? */ char *s = strchr(S->rdline, '>'); if (s) { /* Ah yes, it well could be.. */ strcpy(S->rdline2, S->rdline); return; } } /* FIXME: re-arrange the S->rdline2 contained AX25 address tokens and flags.. perl code: @addrs = split('>', $rdline2); $out = shift @addrs; # pop first token in sequence $out .= '>'; $out .= pop @addrs; # pop last token in sequence foreach $a (@addrs) { # rest of the tokens in sequence, if any $out .= ',' . $a; } # now $out has address data in TNC2 sequence. */ /* printf("%s%s\n", S->rdline2, S->rdline); fflush(stdout); */ return 0; } #endif /* * ttyreader_pulltext() -- process a line of text from the serial port.. */ static int ttyreader_pulltext(struct serialport *S) { int c; const time_t rdtime = S->rdline_time; // "rdtime > now" case ("now" going backwards) is always overwritten below if (timecmp(rdtime+2, tick.tv_sec) < 0) { // A timeout has happen? Either data is added constantly, or // nothing was received from TEXT datastream for couple seconds! S->rdlinelen = 0; // S->kissstate = KISSSTATE_SYNCHUNT; } S->rdline_time = tick.tv_sec; for (;;) { c = ttyreader_getc(S); if (c < 0) return c; /* Out of input.. */ /* S->kissstate != 0: read data into S->rdline, == 0: discard data until CR|LF. Zero-size read line is discarded as well (only CR|LF on input frame) */ if (S->kissstate == KISSSTATE_SYNCHUNT) { /* Looking for CR or LF.. */ if (c == '\n' || c == '\r') S->kissstate = KISSSTATE_COLLECTING; S->rdlinelen = 0; continue; } /* Now: (S->kissstate != KISSSTATE_SYNCHUNT) */ if (c == '\n' || c == '\r') { /* End of line seen! */ if (S->rdlinelen > 0) { /* Non-zero-size string, put terminating 0 byte on it. */ S->rdline[S->rdlinelen] = 0; /* .. and process it depending .. */ if (S->linetype == LINETYPE_TNC2) { ttyreader_pulltnc2(S); #if 0 } else { /* .. it is LINETYPE_AEA ? */ ttyreader_pullaea(S); #endif } } S->rdlinelen = 0; continue; } /* Now place the char in the linebuffer, if there is space.. */ if (S->rdlinelen >= (sizeof(S->rdline) - 3)) { /* Too long ! Way too long ! */ S->kissstate = KISSSTATE_SYNCHUNT; /* Sigh.. discard it. */ S->rdlinelen = 0; continue; } /* Put it on line store: */ S->rdline[S->rdlinelen++] = c; } /* .. input loop */ return 0; /* not reached */ } /* * ttyreader_linewrite() -- write out buffered data */ void ttyreader_linewrite(struct serialport *S) { int i, len; if ((S->wrlen == 0) || (S->wrlen > 0 && S->wrcursor >= S->wrlen)) { S->wrlen = S->wrcursor = 0; /* already all written */ return; } /* Now there is some data in between wrcursor and wrlen */ len = S->wrlen - S->wrcursor; if (len > 0) i = write(S->fd, S->wrbuf + S->wrcursor, len); else i = 0; if (i > 0) { /* wrote something */ S->wrcursor += i; len = S->wrlen - S->wrcursor; if (len == 0) { S->wrcursor = S->wrlen = 0; /* wrote all ! */ } else { /* compact the buffer a bit */ memcpy(S->wrbuf, S->wrbuf + S->wrcursor, len); S->wrcursor = 0; S->wrlen = len; } } } /* * ttyreader_lineread() -- read what there is into our buffer, * and process the buffer.. */ static void ttyreader_lineread(struct serialport *S) { int i; int rdspace = sizeof(S->rdbuf) - S->rdlen; if (S->rdcursor > 0) { /* Read-out cursor is not at block beginning, is there unread data too ? */ if (S->rdlen > S->rdcursor) { /* Uh.. lets move buffer down a bit, to make room for more to the end.. */ memcpy(S->rdbuf, S->rdbuf + S->rdcursor, S->rdlen - S->rdcursor); S->rdlen = S->rdlen - S->rdcursor; } else S->rdlen = 0; /* all processed, mark its size zero */ /* Cursor to zero, rdspace recalculated */ S->rdcursor = 0; /* recalculate */ rdspace = sizeof(S->rdbuf) - S->rdlen; } if (rdspace > 0) { /* We have room to read into.. */ i = read(S->fd, S->rdbuf + S->rdlen, rdspace); if (i == 0) { /* EOF ? USB unplugged ? */ close(S->fd); S->fd = -1; tv_timeradd_seconds(&S->wait_until, &tick, TTY_OPEN_RETRY_DELAY_SECS); aprxlog("TTY %s EOF - CLOSED, WAITING %d SECS\n", S->ttyname, TTY_OPEN_RETRY_DELAY_SECS); return; } if (i < 0) /* EAGAIN or whatever.. */ return; /* Some data has been accumulated ! */ if (debug > 2) { printf("%ld\tTTY %s: read() frame: ", tick.tv_sec, S->ttyname); hexdumpfp(stdout, S->rdbuf+S->rdlen, i, 1); printf("\n"); } S->rdlen += i; S->last_read_something = tick.tv_sec; } /* Done reading, maybe. Now processing. The pullXX does read up all input, and does however many frames there are in, and pauses when there is no enough input data for a full frame/line/whatever. */ if (S->linetype == LINETYPE_KISS || S->linetype == LINETYPE_KISSFLEXNET || S->linetype == LINETYPE_KISSBPQCRC || S->linetype == LINETYPE_KISSSMACK) { kiss_pullkiss(S); #ifndef DISABLE_IGATE } else if (S->linetype == LINETYPE_DPRSGW) { dprsgw_pulldprs(S); #endif } else if (S->linetype == LINETYPE_TNC2 #if 0 || S->linetype == LINETYPE_AEA #endif ) { ttyreader_pulltext(S); } else { close(S->fd); /* Urgh ?? Bad linetype value ?? */ S->fd = -1; tv_timeradd_seconds(&S->wait_until, &tick, TTY_OPEN_RETRY_DELAY_SECS); aprxlog("TTY %s Unsupported linetype - CLOSED, WAITING %d SECS\n", S->ttyname, TTY_OPEN_RETRY_DELAY_SECS); } /* Consumed something, and our read cursor is not in the beginning ? */ if (S->rdcursor > 0 && S->rdcursor < S->rdlen) { /* Compact the input buffer! */ memcpy(S->rdbuf, S->rdbuf + S->rdcursor, S->rdlen - S->rdcursor); } S->rdlen -= S->rdcursor; S->rdcursor = 0; } /* * ttyreader_linesetup() -- open and configure the serial port */ static void ttyreader_linesetup(struct serialport *S) { int i; S->wait_until.tv_sec = 0; // Zero it just to be safe S->wait_until.tv_usec = 0; // Zero it just to be safe S->wrlen = S->wrcursor = 0; // init them at first // If NOT tcp! type socket, it is presumably openable with // open(2) instead of something else, like socket(2)... if (memcmp(S->ttyname, "tcp!", 4) != 0) { int e; // Open the serial port as RW, non-blocking, no-control-tty S->fd = open(S->ttyname, O_RDWR | O_NOCTTY | O_NONBLOCK, 0); e = errno; if (debug) { printf("%ld\tTTY %s OPEN - fd=%d - ", tick.tv_sec, S->ttyname, S->fd); if (S->fd < 0) { printf("errno=%d (%s) - ", e, strerror(e)); } } if (S->fd < 0) { /* Urgh.. an error.. */ tv_timeradd_seconds(&S->wait_until, &tick, TTY_OPEN_RETRY_DELAY_SECS); if (debug) printf("FAILED, WAITING %d SECS\n", TTY_OPEN_RETRY_DELAY_SECS); aprxlog("TTY %s failed to open; errno=%d (%s)", S->ttyname, e, strerror(e)); return; } if (debug) printf("OK\n"); aprxlog("TTY %s Opened.\n", S->ttyname); /* Set attributes */ aprx_cfmakeraw(&S->tio, 1); /* hw-flow on */ i = tcsetattr(S->fd, TCSAFLUSH, &S->tio); if (i < 0) { if (debug) printf("%ld\tERROR: TCSETATTR failed; errno=%d\n", tick.tv_sec, errno); close(S->fd); S->fd = -1; tv_timeradd_seconds(&S->wait_until, &tick, TTY_OPEN_RETRY_DELAY_SECS); aprxlog("TTY %s tcsetattr() failed. CLOSING TTY.\n", S->ttyname); return; } // FIXME: ?? Set baud-rates ? // Used system (Linux) has them in 'struct termios' so they // are now set, but other systems may have different ways.. // Flush buffers once again. i = tcflush(S->fd, TCIOFLUSH); for (i = 0; i < 16; ++i) { if (S->initstring[i] != NULL) { memcpy(S->wrbuf + S->wrlen, S->initstring[i], S->initlen[i]); S->wrlen += S->initlen[i]; } } /* Flush it out.. and if not successfull, poll(2) will take care of it soon enough.. */ ttyreader_linewrite(S); } else { /* socket connection to remote TTY.. */ /* "tcp!hostname-or-ip!port!opt-parameters" */ char *par = strdup(S->ttyname); char *host = NULL, *port = NULL, *opts = NULL; struct addrinfo req, *ai; int i; if (debug) printf("socket connect() preparing: %s\n", par); while (1) { host = strchr(par, '!'); if (host) ++host; else break; /* Found no '!' ! */ port = strchr(host, '!'); if (port) *port++ = 0; else break; /* Found no '!' ! */ opts = strchr(port, '!'); if (opts) *opts++ = 0; break; } if (!port) { /* Still error condition.. no port data */ } memset(&req, 0, sizeof(req)); req.ai_socktype = SOCK_STREAM; req.ai_protocol = IPPROTO_TCP; req.ai_flags = 0; #if 1 req.ai_family = AF_UNSPEC; /* IPv4 and IPv6 are both OK */ #else req.ai_family = AF_INET; /* IPv4 only */ #endif ai = NULL; i = getaddrinfo(host, port, &req, &ai); if (ai) { S->fd = socket(ai->ai_family, SOCK_STREAM, 0); if (S->fd >= 0) { fd_nonblockingmode(S->fd); i = connect(S->fd, ai->ai_addr, ai->ai_addrlen); if ((i != 0) && (errno != EINPROGRESS)) { /* non-blocking connect() yields EINPROGRESS, anything else and we fail entirely... */ if (debug) printf("ttyreader socket connect call failed: %d : %s\n", errno, strerror(errno)); close(S->fd); S->fd = -1; aprxlog("TTY %s Socket open failed.\n", S->ttyname); } } freeaddrinfo(ai); } free(par); } S->last_read_something = tick.tv_sec; /* mark the timeout for future.. */ S->rdlen = S->rdcursor = S->rdlinelen = 0; S->kissstate = KISSSTATE_SYNCHUNT; memset( S->smack_probe, 0, sizeof(S->smack_probe) ); S->smack_subids = 0; } /* * ttyreader_init() */ void ttyreader_init(void) { /* nothing.. */ } /* * ttyreader_prepoll() -- prepare system for next round of polling */ int ttyreader_prepoll(struct aprxpolls *app) { int idx = 0; /* returns number of *fds filled.. */ int i; struct serialport *S; struct pollfd *pfd; if (poll_millis_tv.tv_sec == 0) { poll_millis_tv = tick; } // if (debug) printf("ttyreader_prepoll() %d\n", poll_millis); for (i = 0; i < ttycount; ++i) { S = ttys[i]; if (!S->ttyname) continue; /* No name, no look... */ #if 0 // occasional debug mode without real hardware at hand if (poll_millis > 0) { int deltams = tv_timerdelta_millis(&tick, &poll_millis_tv); struct timeval tv; if (debug) printf("%d.%06d .. defining %d ms KISS POLL\n", tick.tv_sec, tick.tv_usec, poll_millis); } #endif if (S->fd < 0) { if (time_reset && (S->wait_until.tv_sec != 0)) { // System time jumped, reset it to NOW. S->wait_until = tick; } /* Not an open TTY, but perhaps waiting ? */ if ((S->wait_until.tv_sec != 0) && tv_timercmp( &S->wait_until, &tick) > 0) { /* .. waiting for future! */ if (tv_timercmp( &app->next_timeout, &S->wait_until ) > 0) { app->next_timeout = S->wait_until; } /* .. but only until our timeout, if it is sooner than global one. */ continue; /* Waiting on this one.. */ } /* Waiting or not, FD is not open, and deadline is past. Lets try to open! */ ttyreader_linesetup(S); } /* .. No open FD */ /* Still no open FD ? */ if (S->fd < 0) continue; // FD is open, check read/idle timeout ... if (time_reset) { // System time has jumped, Reset the read time to NOW. S->last_read_something = tick.tv_sec; } // FD is open, check read/idle timeout ... if ((S->read_timeout > 0) && timecmp(tick.tv_sec, (S->last_read_something + S->read_timeout)) > 0) { if (debug) printf("%ld\tRead timeout on %s; %d seconds w/o input. fd=%d\n", tick.tv_sec, S->ttyname, S->read_timeout, S->fd); close(S->fd); /* Close and mark for re-open */ S->fd = -1; tv_timeradd_seconds( &S->wait_until, &tick, TTY_OPEN_RETRY_DELAY_SECS); aprxlog("TTY %s read timeout. Closing TTY for later re-open.\n", S->ttyname); continue; } if (poll_millis > 0) { int margin = poll_millis*2; // Limit large delta time to within 0..2*poll_millis. int deltams = tv_timerdelta_millis(&tick, &poll_millis_tv); if (deltams > margin) deltams = poll_millis; if (deltams < -margin) deltams = poll_millis; tv_timeradd_millis(&poll_millis_tv, &tick, deltams); if (debug) printf("%ld.%06d .. defining %d ms KISS POLL\n", (long)tick.tv_sec, (int)tick.tv_usec, poll_millis); } /* FD is open, lets mark it for poll read.. */ pfd = aprxpolls_new(app); pfd->fd = S->fd; pfd->events = POLLIN | POLLPRI; pfd->revents = 0; if (S->wrlen > 0 && S->wrlen > S->wrcursor) pfd->events |= POLLOUT; ++idx; } return idx; } /* * ttyreader_postpoll() -- Done polling, what happened ? */ int ttyreader_postpoll(struct aprxpolls *app) { int idx, i; struct serialport *S; struct pollfd *P; // if (debug) printf("ttyreader_postpoll()\n"); for (idx = 0, P = app->polls; idx < app->pollcount; ++idx, ++P) { // Are we operating in active KISS polling mode? if (poll_millis > 0) { for (i = 0; i < ttycount; ++i) { S = ttys[i]; #if 0 // occasional debug mode without real hardware at hand if (tv_timercmp(&poll_millis_tv, &tick) <= 0) { // Poll interval gone, time for next active POLL request! kiss_poll(S); tv_timeradd_millis(&poll_millis_tv, &poll_millis_tv, poll_millis); } #endif if (S->fd != P->fd) continue; /* Not this one ? */ if (S->fd < 0) continue; /* Not this one ? */ if (!(S->linetype == LINETYPE_KISS || S->linetype == LINETYPE_KISSFLEXNET || S->linetype == LINETYPE_KISSBPQCRC || S->linetype == LINETYPE_KISSSMACK)) { // Not a KISS line.. continue; } if (tv_timercmp(&poll_millis_tv, &tick) <= 0) { // Poll interval gone, time for next active POLL request! kiss_poll(S); tv_timeradd_millis(&poll_millis_tv, &poll_millis_tv, poll_millis); } } } for (i = 0; i < ttycount; ++i) { S = ttys[i]; if (S->fd != P->fd) continue; /* Not this one ? */ /* It is this one! */ if (P->revents & POLLOUT) ttyreader_linewrite(S); if (P->revents & (POLLIN | POLLPRI | POLLERR | POLLHUP)) ttyreader_lineread(S); } } return 0; } /* * Make a pre-existing termios structure into "raw" mode: character-at-a-time * mode with no characters interpreted, 8-bit data path. */ void aprx_cfmakeraw(t, f) struct termios *t; { t->c_iflag &= ~(IMAXBEL|IXOFF|INPCK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IGNPAR); t->c_iflag |= IGNBRK; t->c_oflag &= ~OPOST; if (f) { t->c_oflag |= CRTSCTS; } else { t->c_oflag &= ~CRTSCTS; } t->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN|NOFLSH|TOSTOP|PENDIN); t->c_cflag &= ~(CSIZE|PARENB); t->c_cflag |= CS8|CREAD; t->c_cc[VMIN] = 80; t->c_cc[VTIME] = 3; } struct serialport *ttyreader_new(void) { struct serialport *tty = calloc(1, sizeof(*tty)); int baud = B1200; tty->fd = -1; tv_timeradd_seconds( &tty->wait_until, &tick, -1); /* begin opening immediately */ tty->last_read_something = tick.tv_sec; /* well, not really.. */ tty->linetype = LINETYPE_KISS; /* default */ tty->kissstate = KISSSTATE_SYNCHUNT; tty->read_timeout = 3600; /* Default port read timeout is 60 minutes. */ tty->ttyname = NULL; /* setup termios parameters for this line.. */ aprx_cfmakeraw(&tty->tio, 0); tty->tio.c_cc[VMIN] = 80; /* pick at least one char .. */ tty->tio.c_cc[VTIME] = 3; /* 0.3 seconds timeout - 36 chars @ 1200 baud */ tty->tio.c_cflag |= (CREAD | CLOCAL); cfsetispeed(&tty->tio, baud); cfsetospeed(&tty->tio, baud); return tty; } /* * Parse tty related parameters, return 0 for OK, 1 for error */ int ttyreader_parse_nullparams(struct configfile *cf, struct serialport *tty, char *str) { char *param1 = 0; int has_fault = 0; /* FIXME: analyze correct serial port data and parity format settings, now hardwired to 8-n-1 -- does not work without for KISS anyway.. */ config_STRLOWER(str); /* until end of line */ /* Optional parameters */ while (*str != 0) { param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (debug) printf(" .. param='%s'",param1); /* Note: param1 is now lower-case string */ if (strcmp(param1, "pollmillis") == 0) { param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); tty->poll_millis = atol(param1); // milliseconds if (poll_millis == 0) poll_millis = tty->poll_millis; if (tty->poll_millis < poll_millis) poll_millis = tty->poll_millis; if (poll_millis < 1 || poll_millis > 10000) { has_fault = 1; printf("%s:%d POLLMILLIS value not in sanity range of 1 to 10 000: '%s'", cf->name, cf->linenum, param1); } else { if (debug) printf(" .. pollmillis %d -- polling interval\n", tty->poll_millis); } } else { printf("%s:%d ERROR: Unknown sub-keyword on a serial/tcp device configuration: '%s'\n", cf->name, cf->linenum, param1); has_fault = 1; } } if (debug) printf("\n"); return has_fault; } /* * Parse tty related parameters, return 0 for OK, 1 for error */ int ttyreader_parse_ttyparams(struct configfile *cf, struct serialport *tty, char *str) { int i; speed_t baud; int tncid = 0; char *param1 = 0; int has_fault = 0; /* FIXME: analyze correct serial port data and parity format settings, now hardwired to 8-n-1 -- does not work without for KISS anyway.. */ config_STRLOWER(str); /* until end of line */ /* Optional parameters */ while (*str != 0) { param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (debug) printf(" .. param='%s'",param1); /* See if it is baud-rate ? */ i = atol(param1); /* serial port speed - baud rate */ baud = B1200; switch (i) { case 1200: baud = B1200; break; #ifdef B1800 case 1800: baud = B1800; break; #endif case 2400: baud = B2400; break; case 4800: baud = B4800; break; case 9600: baud = B9600; break; #ifdef B19200 case 19200: baud = B19200; break; #endif #ifdef B38400 case 38400: baud = B38400; break; #endif #ifdef B57600 case 57600: baud = B57600; break; #endif #ifdef B115200 case 115200: baud = B115200; break; #endif #ifdef B230400 case B230400: baud = B230400; break; #endif #ifdef B460800 case 460800: baud = B460800; break; #endif #ifdef B500000 case 500000: baud = B500000; break; #endif #ifdef B576000 case 576000: baud = B576000; break; #endif default: i = -1; break; } if (baud != B1200) { cfsetispeed(&tty->tio, baud); cfsetospeed(&tty->tio, baud); } /* Note: param1 is now lower-case string */ if (i > 0) { ; } else if (strcmp(param1, "8n1") == 0) { /* default behaviour, ignore */ } else if (strcmp(param1, "kiss") == 0) { tty->linetype = LINETYPE_KISS; /* plain basic KISS */ } else if (strcmp(param1, "xorsum") == 0) { tty->linetype = LINETYPE_KISSBPQCRC; /* KISS with BPQ "CRC" */ } else if (strcmp(param1, "xkiss") == 0) { tty->linetype = LINETYPE_KISSBPQCRC; /* KISS with BPQ "CRC" */ } else if (strcmp(param1, "bpqcrc") == 0) { tty->linetype = LINETYPE_KISSBPQCRC; /* KISS with BPQ "CRC" */ } else if (strcmp(param1, "flexnet") == 0) { tty->linetype = LINETYPE_KISSFLEXNET; /* KISS with FLEXNET's CRC16 */ } else if (strcmp(param1, "smack") == 0) { tty->linetype = LINETYPE_KISSSMACK; /* KISS with SMACK / CRC16 */ } else if (strcmp(param1, "crc16") == 0) { tty->linetype = LINETYPE_KISSSMACK; /* KISS with SMACK / CRC16 */ } else if (strcmp(param1, "poll") == 0) { /* FIXME: Some systems want polling... */ } else if (strcmp(param1, "callsign") == 0 || strcmp(param1, "alias") == 0) { param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRUPPER(param1); tty->ttycallsign[tncid] = strdup(param1); #ifdef PF_AX25 /* PF_AX25 exists -- highly likely a Linux system ! */ tty->netax25[tncid] = netax25_open(param1); #endif /* Use side-effect: this defines the tty into erlang accounting */ erlang_set(param1, /* Heuristic constant for max channel capa.. */ (int) ((1200.0 * 60) / 8.2)); } else if (strcmp(param1, "timeout") == 0) { param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); tty->read_timeout = atol(param1); } else if (strcmp(param1, "tncid") == 0) { param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); tncid = atoi(param1); if (tncid < 0 || tncid > 15) { tncid = 0; printf("%s:%d TNCID value not in sanity range of 0 to 15: '%s'", cf->name, cf->linenum, param1); has_fault = 1; } } else if (strcmp(param1, "pollmillis") == 0) { param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); tty->poll_millis = atol(param1); // milliseconds if (poll_millis == 0) poll_millis = tty->poll_millis; if (tty->poll_millis < poll_millis) poll_millis = tty->poll_millis; if (poll_millis < 1 || poll_millis > 10000) { has_fault = 1; printf("%s:%d POLLMILLIS value not in sanity range of 1 to 10 000: '%s'", cf->name, cf->linenum, param1); } else { if (debug) printf(" .. pollmillis %d -- polling interval\n", tty->poll_millis); } #ifndef DISABLE_IGATE } else if (strcmp(param1, "tnc2") == 0) { tty->linetype = LINETYPE_TNC2; /* TNC2 monitor */ } else if (strcmp(param1, "dprs") == 0) { tty->linetype = LINETYPE_DPRSGW; #endif } else if (strcmp(param1, "initstring") == 0) { int parlen; param1 = str; str = config_SKIPTEXT(str, &parlen); str = config_SKIPSPACE(str); tty->initlen[tncid] = parlen; tty->initstring[tncid] = malloc(parlen); memcpy(tty->initstring[tncid], param1, parlen); if (debug) printf("initstring len=%d\n",parlen); } else { printf("%s:%d ERROR: Unknown sub-keyword on a serial/tcp device configuration: '%s'\n", cf->name, cf->linenum, param1); has_fault = 1; } } if (debug) printf("\n"); return has_fault; } void ttyreader_register(struct serialport *tty) { /* Grow the array as is needed.. - this is array of pointers, not array of blocks so that memory allocation does not grow into way too big chunks. */ ttys = realloc(ttys, sizeof(void *) * (ttycount + 1)); ttys[ttycount++] = tty; } const char *ttyreader_serialcfg(struct configfile *cf, char *param1, char *str) { /* serialport /dev/ttyUSB123 19200 8n1 {KISS|TNC2|AEA|..} */ struct serialport *tty; /* radio serial /dev/ttyUSB123 [19200 [8n1]] KISS radio tcp 12.34.56.78 4001 KISS */ if (*param1 == 0) return "Bad mode keyword"; if (*str == 0) return "Bad tty-name/param"; tty = ttyreader_new(); ttyreader_register(tty); if (strcmp(param1, "serial") == 0) { /* New style! */ free((char *) (tty->ttyname)); param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); tty->ttyname = strdup(param1); if (debug) printf(".. new style serial: '%s' '%s'..\n", tty->ttyname, str); } else if (strcmp(param1, "tcp") == 0) { /* New style! */ int len; char *host, *port; free((char *) (tty->ttyname)); host = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); port = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (debug) printf(".. new style tcp!: '%s' '%s' '%s'..\n", host, port, str); len = strlen(host) + strlen(port) + 8; tty->ttyname = malloc(len); sprintf((char *) (tty->ttyname), "tcp!%s!%s!", host, port); } if (ttyreader_parse_ttyparams( cf, tty, str)) return "Bad ttyparameters"; return NULL; // All OK } aprx-2.08.svn593/ssl.c0000644000175000017500000005664512273250100013432 0ustar colincolin /* * This OpenSSL interface code has been proudly copied from * the excellent NGINX web server. * * Its license is reproduced here. */ /* * Copyright (C) 2002-2013 Igor Sysoev * Copyright (C) 2011-2013 Nginx, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * OpenSSL thread-safe example code (ssl_thread_* functions) have been * proudly copied from the excellent CURL package, the original author * is Jeremy Brown. * * https://github.com/bagder/curl/blob/master/docs/examples/opensslthreadlock.c * * COPYRIGHT AND PERMISSION NOTICE * Copyright (c) 1996 - 2013, Daniel Stenberg, . * * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any purpose * with or without fee is hereby granted, provided that the above copyright * notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE * OR OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of a copyright holder shall not * be used in advertising or otherwise to promote the sale, use or other dealings * in this Software without prior written authorization of the copyright holder. * */ #include "config.h" #include "ssl.h" #include "hlog.h" #include "hmalloc.h" #include "worker.h" #ifdef USE_SSL #include #include #include #include #include #define SSL_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5" #define SSL_PROTOCOLS (NGX_SSL_SSLv3|NGX_SSL_TLSv1 |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2) /* ssl error strings */ #define SSL_ERR_LABELS_COUNT 6 static const char *ssl_err_labels[][2] = { { "invalid_err", "Invalid, unknown error" }, { "internal_err", "Internal error" }, { "peer_cert_unverified", "Peer certificate is not valid or not trusted" }, { "no_peer_cert", "Peer did not present a certificate" }, { "cert_no_subj", "Certificate does not contain a Subject field" }, { "cert_no_callsign", "Certificate does not contain a TQSL callsign in CN - not a ham cert" }, { "cert_callsign_mismatch", "Certificate callsign does not match login username" } }; /* pthread wrapping for openssl */ #define MUTEX_TYPE pthread_mutex_t #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) #define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) #define THREAD_ID pthread_self( ) int ssl_available; int ssl_connection_index; int ssl_server_conf_index; int ssl_session_cache_index; /* This array will store all of the mutexes available to OpenSSL. */ static MUTEX_TYPE *mutex_buf= NULL; static void ssl_thread_locking_function(int mode, int n, const char * file, int line) { if (mode & CRYPTO_LOCK) MUTEX_LOCK(mutex_buf[n]); else MUTEX_UNLOCK(mutex_buf[n]); } static unsigned long ssl_thread_id_function(void) { return ((unsigned long)THREAD_ID); } static int ssl_thread_setup(void) { int i; hlog(LOG_DEBUG, "Creating OpenSSL mutexes (%d)...", CRYPTO_num_locks()); mutex_buf = hmalloc(CRYPTO_num_locks() * sizeof(MUTEX_TYPE)); for (i = 0; i < CRYPTO_num_locks(); i++) MUTEX_SETUP(mutex_buf[i]); CRYPTO_set_id_callback(ssl_thread_id_function); CRYPTO_set_locking_callback(ssl_thread_locking_function); return 0; } static int ssl_thread_cleanup(void) { int i; if (!mutex_buf) return 0; CRYPTO_set_id_callback(NULL); CRYPTO_set_locking_callback(NULL); for (i = 0; i < CRYPTO_num_locks( ); i++) MUTEX_CLEANUP(mutex_buf[i]); hfree(mutex_buf); mutex_buf = NULL; return 0; } /* * string representations for error codes */ const char *ssl_strerror(int code) { code *= -1; if (code >= 0 && code < (sizeof ssl_err_labels / sizeof ssl_err_labels[0])) return ssl_err_labels[code][1]; return ssl_err_labels[0][1]; } /* * Clear OpenSSL error queue */ static void ssl_error(int level, const char *msg) { unsigned long n; char errstr[512]; for ( ;; ) { n = ERR_get_error(); if (n == 0) break; ERR_error_string_n(n, errstr, sizeof(errstr)); errstr[sizeof(errstr)-1] = 0; hlog(level, "%s (%d): %s", msg, n, errstr); } } static void ssl_clear_error(void) { while (ERR_peek_error()) { ssl_error(LOG_INFO, "Ignoring stale SSL error"); } ERR_clear_error(); } /* * TrustedQSL custom X.509 certificate objects */ #define TRUSTEDQSL_OID "1.3.6.1.4.1.12348.1." #define TRUSTEDQSL_OID_CALLSIGN TRUSTEDQSL_OID "1" #define TRUSTEDQSL_OID_QSO_NOT_BEFORE TRUSTEDQSL_OID "2" #define TRUSTEDQSL_OID_QSO_NOT_AFTER TRUSTEDQSL_OID "3" #define TRUSTEDQSL_OID_DXCC_ENTITY TRUSTEDQSL_OID "4" #define TRUSTEDQSL_OID_SUPERCEDED_CERT TRUSTEDQSL_OID "5" #define TRUSTEDQSL_OID_CRQ_ISSUER_ORGANIZATION TRUSTEDQSL_OID "6" #define TRUSTEDQSL_OID_CRQ_ISSUER_ORGANIZATIONAL_UNIT TRUSTEDQSL_OID "7" static const char *tqsl_NIDs[][2] = { { TRUSTEDQSL_OID_CALLSIGN, "AROcallsign" }, { TRUSTEDQSL_OID_QSO_NOT_BEFORE, "QSONotBeforeDate" }, { TRUSTEDQSL_OID_QSO_NOT_AFTER, "QSONotAfterDate" }, { TRUSTEDQSL_OID_DXCC_ENTITY, "dxccEntity" }, { TRUSTEDQSL_OID_SUPERCEDED_CERT, "supercededCertificate" }, { TRUSTEDQSL_OID_CRQ_ISSUER_ORGANIZATION, "tqslCRQIssuerOrganization" }, { TRUSTEDQSL_OID_CRQ_ISSUER_ORGANIZATIONAL_UNIT, "tqslCRQIssuerOrganizationalUnit" }, }; static int load_tqsl_custom_objects(void) { int i; for (i = 0; i < (sizeof tqsl_NIDs / sizeof tqsl_NIDs[0]); ++i) if (OBJ_create(tqsl_NIDs[i][0], tqsl_NIDs[i][1], NULL) == 0) return -1; return 0; } static void ssl_info_callback(SSL *ssl, int where, int ret) { struct client_t *c = SSL_get_ex_data(ssl, ssl_connection_index); if (!c) { hlog(LOG_ERR, "ssl_info_callback: no application data for connection"); return; } struct ssl_connection_t *ssl_conn = c->ssl_con; if (!ssl_conn) { hlog(LOG_ERR, "ssl_info_callback: no ssl_conn for connection"); return; } if (where & SSL_CB_HANDSHAKE_START) { hlog(LOG_INFO, "%s/%d: SSL handshake start", c->addr_rem, c->fd); if (ssl_conn->handshaked) { ssl_conn->renegotiation = 1; } } if (where & SSL_CB_HANDSHAKE_DONE) { hlog(LOG_INFO, "%s/%d: SSL handshake done", c->addr_rem, c->fd); } } /* * Initialize SSL */ int ssl_init(void) { hlog(LOG_INFO, "Initializing OpenSSL, built against %s ...", OPENSSL_VERSION_TEXT); OPENSSL_config(NULL); SSL_library_init(); SSL_load_error_strings(); ssl_thread_setup(); OpenSSL_add_all_algorithms(); load_tqsl_custom_objects(); #if OPENSSL_VERSION_NUMBER >= 0x0090800fL #ifndef SSL_OP_NO_COMPRESSION { /* * Disable gzip compression in OpenSSL prior to 1.0.0 version, * this saves about 522K per connection. */ int n; STACK_OF(SSL_COMP) *ssl_comp_methods; ssl_comp_methods = SSL_COMP_get_compression_methods(); n = sk_SSL_COMP_num(ssl_comp_methods); while (n--) { (void) sk_SSL_COMP_pop(ssl_comp_methods); } } #endif #endif ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ssl_connection_index == -1) { ssl_error(LOG_ERR, "SSL_get_ex_new_index for connection"); return -1; } ssl_server_conf_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ssl_server_conf_index == -1) { ssl_error(LOG_ERR, "SSL_CTX_get_ex_new_index for conf"); return -1; } ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ssl_session_cache_index == -1) { ssl_error(LOG_ERR, "SSL_CTX_get_ex_new_index for session cache"); return -1; } ssl_available = 1; return 0; } void ssl_atend(void) { ssl_thread_cleanup(); } struct ssl_t *ssl_alloc(void) { struct ssl_t *ssl; ssl = hmalloc(sizeof(*ssl)); memset(ssl, 0, sizeof(*ssl)); return ssl; } void ssl_free(struct ssl_t *ssl) { if (ssl->ctx) SSL_CTX_free(ssl->ctx); hfree(ssl); } int ssl_create(struct ssl_t *ssl, void *data) { ssl->ctx = SSL_CTX_new(SSLv23_method()); if (ssl->ctx == NULL) { ssl_error(LOG_ERR, "ssl_create SSL_CTX_new failed"); return -1; } if (SSL_CTX_set_ex_data(ssl->ctx, ssl_server_conf_index, data) == 0) { ssl_error(LOG_ERR, "ssl_create SSL_CTX_set_ex_data failed"); return -1; } /* client side options */ SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_SESS_ID_BUG); SSL_CTX_set_options(ssl->ctx, SSL_OP_NETSCAPE_CHALLENGE_BUG); /* server side options */ SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG); SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); /* this option allow a potential SSL 2.0 rollback (CAN-2005-2969) */ SSL_CTX_set_options(ssl->ctx, SSL_OP_MSIE_SSLV2_RSA_PADDING); SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLEAY_080_CLIENT_DH_BUG); SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_D5_BUG); SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_BLOCK_PADDING_BUG); SSL_CTX_set_options(ssl->ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE); /* SSL protocols not configurable for now */ int protocols = SSL_PROTOCOLS; if (!(protocols & NGX_SSL_SSLv2)) { SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2); } if (!(protocols & NGX_SSL_SSLv3)) { SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv3); } if (!(protocols & NGX_SSL_TLSv1)) { SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1); } #ifdef SSL_OP_NO_TLSv1_1 if (!(protocols & NGX_SSL_TLSv1_1)) { SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_1); } #endif #ifdef SSL_OP_NO_TLSv1_2 if (!(protocols & NGX_SSL_TLSv1_2)) { SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_2); } #endif #ifdef SSL_OP_NO_COMPRESSION SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_COMPRESSION); #endif #ifdef SSL_MODE_RELEASE_BUFFERS SSL_CTX_set_mode(ssl->ctx, SSL_MODE_RELEASE_BUFFERS); #endif SSL_CTX_set_mode(ssl->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_read_ahead(ssl->ctx, 1); SSL_CTX_set_info_callback(ssl->ctx, (void *)ssl_info_callback); if (SSL_CTX_set_cipher_list(ssl->ctx, SSL_DEFAULT_CIPHERS) == 0) { ssl_error(LOG_ERR, "ssl_create SSL_CTX_set_cipher_list failed"); return -1; } /* prefer server-selected ciphers */ SSL_CTX_set_options(ssl->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); return 0; } /* * Load server key and certificate */ int ssl_certificate(struct ssl_t *ssl, const char *certfile, const char *keyfile) { if (SSL_CTX_use_certificate_chain_file(ssl->ctx, certfile) == 0) { hlog(LOG_ERR, "Error while loading SSL certificate chain file \"%s\"", certfile); ssl_error(LOG_ERR, "SSL_CTX_use_certificate_chain_file"); return -1; } if (SSL_CTX_use_PrivateKey_file(ssl->ctx, keyfile, SSL_FILETYPE_PEM) == 0) { hlog(LOG_ERR, "Error while loading SSL private key file \"%s\"", keyfile); ssl_error(LOG_ERR, "SSL_CTX_use_PrivateKey_file"); return -1; } if (!SSL_CTX_check_private_key(ssl->ctx)) { hlog(LOG_ERR, "SSL private key (%s) does not work with this certificate (%s)", keyfile, certfile); ssl_error(LOG_ERR, "SSL_CTX_check_private_key"); return -1; } return 0; } static int ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) { hlog(LOG_DEBUG, "ssl_verify_callback, ok: %d", ok); #if (NGX_DEBUG) char *subject, *issuer; int err, depth; X509 *cert; X509_NAME *sname, *iname; ngx_connection_t *c; ngx_ssl_conn_t *ssl_conn; ssl_conn = X509_STORE_CTX_get_ex_data(x509_store, SSL_get_ex_data_X509_STORE_CTX_idx()); c = ngx_ssl_get_connection(ssl_conn); cert = X509_STORE_CTX_get_current_cert(x509_store); err = X509_STORE_CTX_get_error(x509_store); depth = X509_STORE_CTX_get_error_depth(x509_store); sname = X509_get_subject_name(cert); subject = sname ? X509_NAME_oneline(sname, NULL, 0) : "(none)"; iname = X509_get_issuer_name(cert); issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)"; ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0, "verify:%d, error:%d, depth:%d, " "subject:\"%s\",issuer: \"%s\"", ok, err, depth, subject, issuer); if (sname) { OPENSSL_free(subject); } if (iname) { OPENSSL_free(issuer); } #endif return 1; } /* * Load trusted CA certs for verifying our peers */ int ssl_ca_certificate(struct ssl_t *ssl, const char *cafile, int depth) { STACK_OF(X509_NAME) *list; SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, ssl_verify_callback); SSL_CTX_set_verify_depth(ssl->ctx, depth); if (SSL_CTX_load_verify_locations(ssl->ctx, cafile, NULL) == 0) { hlog(LOG_ERR, "Failed to load trusted CA list from \"%s\"", cafile); ssl_error(LOG_ERR, "SSL_CTX_load_verify_locations"); return -1; } list = SSL_load_client_CA_file(cafile); if (list == NULL) { hlog(LOG_ERR, "Failed to load client CA file from \"%s\"", cafile); ssl_error(LOG_ERR, "SSL_load_client_CA_file"); return -1; } /* * before 0.9.7h and 0.9.8 SSL_load_client_CA_file() * always leaved an error in the error queue */ ERR_clear_error(); SSL_CTX_set_client_CA_list(ssl->ctx, list); ssl->validate = 1; return 0; } /* * Create a connect */ int ssl_create_connection(struct ssl_t *ssl, struct client_t *c, int i_am_client) { struct ssl_connection_t *sc; sc = hmalloc(sizeof(*sc)); sc->connection = SSL_new(ssl->ctx); if (sc->connection == NULL) { ssl_error(LOG_ERR, "SSL_new failed"); hfree(sc); return -1; } if (SSL_set_fd(sc->connection, c->fd) == 0) { ssl_error(LOG_ERR, "SSL_set_fd failed"); SSL_free(sc->connection); hfree(sc); return -1; } if (i_am_client) { SSL_set_connect_state(sc->connection); } else { SSL_set_accept_state(sc->connection); } if (SSL_set_ex_data(sc->connection, ssl_connection_index, c) == 0) { ssl_error(LOG_ERR, "SSL_set_ex_data failed"); SSL_free(sc->connection); hfree(sc); return -1; } sc->validate = ssl->validate; c->ssl_con = sc; return 0; } void ssl_free_connection(struct client_t *c) { if (!c->ssl_con) return; SSL_free(c->ssl_con->connection); hfree(c->ssl_con); c->ssl_con = NULL; } int ssl_cert_callsign_match(const char *subj_call, const char *username) { if (subj_call == NULL || username == NULL) return 0; while (*username != '-' && *username != 0 && *subj_call != 0) { if (toupper(*username) != toupper(*subj_call)) return 0; /* mismatch */ subj_call++; username++; } if (*subj_call != 0) return 0; /* if username is shorter than subject callsign, we fail */ if (*username != '-' && *username != 0) return 0; /* if we ran to end of subject callsign but not to end of username or start of SSID, we fail */ return 1; } /* * Validate client certificate */ int ssl_validate_peer_cert_phase1(struct client_t *c) { X509 *cert; int rc = SSL_get_verify_result(c->ssl_con->connection); if (rc != X509_V_OK) { /* client gave a certificate, but it's not valid */ hlog(LOG_DEBUG, "%s/%s: Peer SSL certificate verification error %d: %s", c->addr_rem, c->username, rc, X509_verify_cert_error_string(rc)); c->ssl_con->ssl_err_code = rc; return SSL_VALIDATE_CLIENT_CERT_UNVERIFIED; } cert = SSL_get_peer_certificate(c->ssl_con->connection); if (cert == NULL) { /* client did not give a certificate */ return SSL_VALIDATE_NO_CLIENT_CERT; } X509_free(cert); return 0; } int ssl_validate_peer_cert_phase2(struct client_t *c) { int ret = -1; X509 *cert; X509_NAME *sname, *iname; char *subject, *issuer; char *subj_cn = NULL; char *subj_call = NULL; int nid, idx; X509_NAME_ENTRY *entry; ASN1_STRING *edata; cert = SSL_get_peer_certificate(c->ssl_con->connection); if (cert == NULL) { /* client did not give a certificate */ return SSL_VALIDATE_NO_CLIENT_CERT; } /* ok, we have a cert, find subject */ sname = X509_get_subject_name(cert); if (!sname) { ret = SSL_VALIDATE_CERT_NO_SUBJECT; goto fail; } subject = X509_NAME_oneline(sname, NULL, 0); /* find tqsl callsign */ nid = OBJ_txt2nid("AROcallsign"); if (nid == NID_undef) { hlog(LOG_ERR, "OBJ_txt2nid could not find NID for AROcallsign"); ret = SSL_VALIDATE_INTERNAL_ERROR; goto fail; } idx = X509_NAME_get_index_by_NID(sname, nid, -1); if (idx == -1) { hlog(LOG_DEBUG, "%s/%s: peer certificate has no callsign: %s", c->addr_rem, c->username, subject); ret = SSL_VALIDATE_CERT_NO_CALLSIGN; goto fail; } entry = X509_NAME_get_entry(sname, idx); if (entry != NULL) { edata = X509_NAME_ENTRY_get_data(entry); if (edata != NULL) ASN1_STRING_to_UTF8((unsigned char **)&subj_call, edata); } /* find CN of subject */ idx = X509_NAME_get_index_by_NID(sname, NID_commonName, -1); if (idx == -1) { hlog(LOG_DEBUG, "%s/%s: peer certificate has no CN: %s", c->addr_rem, c->username, subject); } else { entry = X509_NAME_get_entry(sname, idx); if (entry != NULL) { edata = X509_NAME_ENTRY_get_data(entry); if (edata != NULL) ASN1_STRING_to_UTF8((unsigned char **)&subj_cn, edata); } } if (!subj_call) { hlog(LOG_DEBUG, "%s/%s: peer certificate callsign conversion failed: %s", c->addr_rem, c->username, subject); ret = SSL_VALIDATE_CERT_NO_CALLSIGN; goto fail; } if (!ssl_cert_callsign_match(subj_call, c->username)) { ret = SSL_VALIDATE_CERT_CALLSIGN_MISMATCH; goto fail; } /* find issuer */ iname = X509_get_issuer_name(cert); issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)"; ret = 0; hlog(LOG_INFO, "%s/%s: Peer validated using SSL certificate: subject '%s' callsign '%s' CN '%s' issuer '%s'", c->addr_rem, c->username, subject, subj_call, (subj_cn) ? subj_cn : "(none)", issuer); /* store copies of cert subject and issuer */ strncpy(c->cert_subject, subject, sizeof(c->cert_subject)); c->cert_subject[sizeof(c->cert_subject)-1] = 0; strncpy(c->cert_issuer, issuer, sizeof(c->cert_issuer)); c->cert_issuer[sizeof(c->cert_issuer)-1] = 0; fail: /* free up whatever we allocated */ X509_free(cert); if (subj_call) OPENSSL_free(subj_call); if (subj_cn) OPENSSL_free(subj_cn); return ret; } /* * Write data to an SSL socket */ int ssl_write(struct worker_t *self, struct client_t *c) { int n; int sslerr; int err; int to_write; to_write = c->obuf_end - c->obuf_start; //hlog(LOG_DEBUG, "ssl_write fd %d of %d bytes", c->fd, to_write); ssl_clear_error(); n = SSL_write(c->ssl_con->connection, c->obuf + c->obuf_start, to_write); //hlog(LOG_DEBUG, "SSL_write fd %d returned %d", c->fd, n); if (n > 0) { /* ok, we wrote some */ c->obuf_start += n; c->obuf_wtime = tick; /* All done ? */ if (c->obuf_start >= c->obuf_end) { //hlog(LOG_DEBUG, "ssl_write fd %d (%s) obuf empty", c->fd, c->addr_rem); c->obuf_start = 0; c->obuf_end = 0; /* tell the poller that we have no outgoing data */ xpoll_outgoing(&self->xp, c->xfd, 0); return n; } xpoll_outgoing(&self->xp, c->xfd, 1); return n; } sslerr = SSL_get_error(c->ssl_con->connection, n); err = (sslerr == SSL_ERROR_SYSCALL) ? errno : 0; if (sslerr == SSL_ERROR_WANT_WRITE) { hlog(LOG_INFO, "ssl_write fd %d: SSL_write wants to write again, marking socket for write events", c->fd); /* tell the poller that we have outgoing data */ xpoll_outgoing(&self->xp, c->xfd, 1); return 0; } if (sslerr == SSL_ERROR_WANT_READ) { hlog(LOG_INFO, "ssl_write fd %d: SSL_write wants to read, returning 0", c->fd); /* tell the poller that we won't be writing now, until we've read... */ xpoll_outgoing(&self->xp, c->xfd, 0); return 0; } if (err) { hlog(LOG_DEBUG, "ssl_write fd %d: I/O syscall error: %s", c->fd, strerror(err)); } else { char ebuf[255]; ERR_error_string_n(sslerr, ebuf, sizeof(ebuf)); hlog(LOG_INFO, "ssl_write fd %d failed with ret %d sslerr %u errno %d: %s (%s)", c->fd, n, sslerr, err, ebuf, ERR_reason_error_string(sslerr)); } c->ssl_con->no_wait_shutdown = 1; c->ssl_con->no_send_shutdown = 1; hlog(LOG_DEBUG, "ssl_write fd %d: SSL_write() failed", c->fd); client_close(self, c, err); return -13; } int ssl_writable(struct worker_t *self, struct client_t *c) { int to_write; to_write = c->obuf_end - c->obuf_start; //hlog(LOG_DEBUG, "ssl_writable fd %d, %d available for writing", c->fd, to_write); /* SSL_write does not appreciate writing a 0-length buffer */ if (to_write == 0) { /* tell the poller that we have no outgoing data */ xpoll_outgoing(&self->xp, c->xfd, 0); return 0; } return ssl_write(self, c); } int ssl_readable(struct worker_t *self, struct client_t *c) { int r; int sslerr, err; //hlog(LOG_DEBUG, "ssl_readable fd %d", c->fd); ssl_clear_error(); r = SSL_read(c->ssl_con->connection, c->ibuf + c->ibuf_end, c->ibuf_size - c->ibuf_end - 1); if (r > 0) { /* we got some data... process */ //hlog(LOG_DEBUG, "SSL_read fd %d returned %d bytes of data", c->fd, r); /* TODO: whatever the client_readable does */ return client_postread(self, c, r); } sslerr = SSL_get_error(c->ssl_con->connection, r); err = (sslerr == SSL_ERROR_SYSCALL) ? errno : 0; if (sslerr == SSL_ERROR_WANT_READ) { hlog(LOG_DEBUG, "ssl_readable fd %d: SSL_read wants to read again, doing it later", c->fd); if (c->obuf_end - c->obuf_start > 0) { /* tell the poller that we have outgoing data */ xpoll_outgoing(&self->xp, c->xfd, 1); } return 0; } if (sslerr == SSL_ERROR_WANT_WRITE) { hlog(LOG_INFO, "ssl_readable fd %d: SSL_read wants to write (peer starts SSL renegotiation?), calling ssl_write", c->fd); return ssl_write(self, c); } c->ssl_con->no_wait_shutdown = 1; c->ssl_con->no_send_shutdown = 1; if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) { hlog(LOG_DEBUG, "ssl_readable fd %d: peer shutdown SSL cleanly", c->fd); client_close(self, c, CLIERR_EOF); return -1; } if (err) { hlog(LOG_DEBUG, "ssl_readable fd %d: I/O syscall error: %s", c->fd, strerror(err)); } else { char ebuf[255]; ERR_error_string_n(sslerr, ebuf, sizeof(ebuf)); hlog(LOG_INFO, "ssl_readable fd %d failed with ret %d sslerr %d errno %d: %s (%s)", c->fd, r, sslerr, err, ebuf, ERR_reason_error_string(sslerr)); } client_close(self, c, err); return -1; } #endif aprx-2.08.svn593/aprx-stat.8.in0000644000175000017500000001337512005774517015117 0ustar colincolin.TH aprx\-stat 8 "@DATEVERSION@" .LO 8 .SH NAME .B aprx\-stat \- statistics utility for .BR aprx (8) .SH SYNOPSIS .B aprx\-stat .RB [ \-t ] .RB [ \-f \fI@VARRUN@/aprx.state\fR] .RB { \-S | \-x | \-X } .SH DESCRIPTION .B aprx\-stat is a statistics utility for .BR aprx (8) program. .SH OPTIONS The .B aprx\-stat has following runtime options: .TP .B "\-f \fI@VARRUN@/aprx.state\fR" Turn on verbose debugging, outputs data to STDOUT. .TP .B "\-S" SNMP data mode, current counter and gauge values. .TP .B "\-t" Use UNIX .I time_t for timestamps, instead of human readable text format. .TP .B "\-x" Lattest of extended historical gauge values. This gives for each input interface .RS .IP \(bu 2 SNMP data .IP \(bu 2 last 90 of 1 minute values, .IP \(bu 2 10 of 10 minute values, .IP \(bu 2 3 of 60 minute values. .RE .TP .B "\-X" Full extended historical gauge values. This gives all the contents of historical value data ring-buffers. .RS .IP \(bu 2 SNMP data .IP \(bu 2 1 minute resolution: 24 hours .IP \(bu 2 10 minute resolution: 7 days .IP \(bu 2 60 minute resolution: 3 months .RE .SH SNMP DATA OUTPUT For each interface feeding AX.25 packets and/or KISS frames to this program, there are following kind of .nf \fC SNMP /dev/ttyUSB1 798282 11088 0 0 3 SNMP ax0 798282 11088 0 0 7 SNMP ax1 798282 11088 0 0 94 .fi .PP where columns are: .IP \(bu 2 "SNMP" - keyword .IP \(bu 2 Interface (AX.25 IF name, or serial port device name) .IP \(bu 2 Received byte counter .IP \(bu 2 Received frame (packet) counter .IP \(bu 2 .\" Transmitted byte counter (will stay zero) .I Dropped byte counter .IP \(bu 2 .\" Transmitted frame counter (will stay zero) .I Dropped frame counter .IP \(bu 2 Age in seconds of last update of this statistics. .SH EXTENDED DATA OUTPUT Extended data output gives formatted historical periodic accumulates of interface traffic counters, and Erlang value estimates based on that. .PP .nf \fC SNMP /dev/ttyUSB1 816675 11332 0 0 15 1min data 2007-12-24 14:10 /dev/ttyUSB1 1m 374 6 0 0 0.047 0.000 2007-12-24 14:09 /dev/ttyUSB1 1m 377 5 0 0 0.047 0.000 2007-12-24 14:08 /dev/ttyUSB1 1m 347 5 0 0 0.043 0.000 2007-12-24 14:07 /dev/ttyUSB1 1m 140 2 0 0 0.018 0.000 \(bu\(bu\(bu 10min data 2007-12-24 14:10 /dev/ttyUSB1 10m 3829 55 0 0 0.048 0.000 2007-12-24 14:00 /dev/ttyUSB1 10m 2182 28 0 0 0.027 0.000 2007-12-24 13:50 /dev/ttyUSB1 10m 3205 44 0 0 0.040 0.000 2007-12-24 13:40 /dev/ttyUSB1 10m 3811 50 0 0 0.048 0.000 \(bu\(bu\(bu 60min data 2007-12-24 14:00 /dev/ttyUSB1 60m 22510 295 0 0 0.047 0.000 2007-12-24 13:00 /dev/ttyUSB1 60m 24886 347 0 0 0.052 0.000 \(bu\(bu\(bu .fi .PP The output repeats for all interfaces. .PP The SNMP dataset is given in the beginning, and described above. Then each extended output line has following fields: .IP \(bu 2 Timestamp is two fields, date and time (in minute resolution) is in UTC. .IP \(bu 2 Alternate timestamp format is UNIX .I time_t as an integer, counting seconds from epoch, and as single field. .IP \(bu 2 Interface name is same as in SNMP case. .IP \(bu 2 Data qualifier tells what integration period the data is valid for: .IR 1m ", " 10m ", " 60m . .IP \(bu 2 Counter of received bytes on interface (including KISS flags etc.) .IP \(bu 2 Counter of received frames. .IP \(bu 2 .\" Counter of transmitted bytes on interface Counter of dropped bytes. .IP \(bu 2 .\" Counter of transmitted frames. Counter of dropped frames. .IP \(bu 2 Reception .I Erlang value estimate. .IP \(bu 2 .\" Transmission Dropped bytes .I Erlang value estimate. .PP .SH TODO .SH BUGS .SH SEE ALSO .BR aprx (8) .SH CONFIGURATION FILE There is no configuration file. .SH NOTES: ERLANG The .I Erlang is telecom measurement of channel occupancy, and in this application sense it does tell how much traffic there is on the radio channel. .PP Most radio transmitters are not aware of all transmitters on channel, and thus there can happen a collision causing loss of both messages. The higher the channel activity, the more likely that collision is. For further details, refer to statistical mathematics books, or perhaps on Wikipedia. .PP In order to measure channel activity, the .B aprx program suite has these built-in statistics counter and summary estimators. .PP The .I Erlag value that the estimators present are likely somewhat .I underestimating the true channel occupancy simply because it calculates estimate of channel bit transmit rate, and thus a per-minute character capacity. It does not know true frequency of bit-stuffing events of the HDLC framing, nor each transmitter pre- and port frame PTT times. The transmitters need to stabilize their transmit oscillators in many cases, which may take up to around 500 ms! The counters are not aware of this preamble-, nor postamble-times. .PP The HDLC bit stuffing ratio is guessed to be 8.2 bits for each 8 bits of payload. .SH NOTES: SUID ROOT This program needs probably to be run as .I "suid\-root" ! It is considered safe to do so, as this checks that the .B "\-f" parameter file is of correct "magic value", and will not try to create it if it does not exist, nor modify that file under any circumstances, nor reveal content of "wrong magic kind" of file. .SH AUTHOR This little piece was written by .I "Matti Aarnio, OH2MQK" during a dark and rainy fall and winter of 2007-2008 after a number of discussions grumbling about current breed of available software for APRS iGate use in Linux (or of any UNIX) platforms. Fall and winter 2009-2010 saw appearance of digipeater functionality. .PP Principal contributors and test users include: .IR "Pentti Gronlund, OH3BK" , .IR "Reijo Hakala, OH1GWK" . Debian packaging by .IR "Kimmo Jukarinen, OH3GNU" . aprx-2.08.svn593/telemetry.c0000644000175000017500000003767212313325035014647 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" #define telemetry_timescaler 2 // scale to 10 minute sums static int telemetry_interval = 20 * 60; // every 20 minutes static int telemetry_labelinterval = 120*60; // every 2 hours static int telemetry_labelindex = 0; #if (defined(ERLANGSTORAGE) || (USE_ONE_MINUTE_STORAGE == 1)) static int telemetry_1min_steps = 20; #endif #if (defined(ERLANGSTORAGE) || (USE_ONE_MINUTE_STORAGE == 0)) static int telemetry_10min_steps = 2; #endif static struct timeval telemetry_time; static struct timeval telemetry_labeltime; static int telemetry_seq; static int telemetry_params; struct rftelemetry { struct aprx_interface *transmitter; struct aprx_interface **sources; int source_count; char *viapath; }; static int rftelemetrycount; static struct rftelemetry **rftelemetry; static void rf_telemetry(const struct aprx_interface *sourceaif, const char *beaconaddr, const const char *buf, const int buflen); static void telemetry_resettime(void *arg) { struct timeval *tv = (struct timeval*)arg; tv_timeradd_seconds( tv, &tick, telemetry_interval ); } static void telemetry_resetlabeltime(void *arg) { struct timeval *tv = (struct timeval*)arg; tv_timeradd_seconds( tv, &tick, 120 ); // first label 2 minutes from now } void telemetry_start() { /* * Initialize the sequence start to be highly likely * different from previous one... This really should * be in some persistent database, but this is reasonable * compromise. */ telemetry_seq = (time(NULL)) & 255; // "tick" is supposedly current time.. telemetry_resettime( &telemetry_time ); telemetry_resetlabeltime( &telemetry_labeltime ); if (debug) printf("telemetry_start()\n"); } int telemetry_prepoll(struct aprxpolls *app) { // Check that time has not jumped too far ahead/back (1.5 telemetry intervals) if (time_reset) { telemetry_resettime(&telemetry_time); telemetry_resetlabeltime(&telemetry_labeltime); } // Normal operational step if (tv_timercmp(&app->next_timeout, &telemetry_time) > 0) app->next_timeout = telemetry_time; if (tv_timercmp(&app->next_timeout, &telemetry_labeltime) > 0) app->next_timeout = telemetry_labeltime; if (debug>1) printf("telemetry_prepoll()\n"); return 0; } static void telemetry_datatx(void); static void telemetry_labeltx(void); int telemetry_postpoll(struct aprxpolls *app) { if (debug>1) { printf("telemetry_postpoll() telemetrytime=%ds labeltime=%ds\n", tv_timerdelta_millis(&tick, &telemetry_time)/1000, tv_timerdelta_millis(&tick, &telemetry_labeltime)/1000); } if (tv_timercmp(&telemetry_time, &tick) <= 0) { tv_timeradd_seconds(&telemetry_time, &telemetry_time, telemetry_interval); telemetry_datatx(); } if (tv_timercmp(&telemetry_labeltime, &tick) <= 0) { tv_timeradd_seconds(&telemetry_labeltime, &telemetry_labeltime, telemetry_labelinterval); telemetry_labeltx(); } return 0; } static void telemetry_datatx(void) { int i, j, k, t; char buf[200], *s; int buflen; char beaconaddr[60]; int beaconaddrlen; long erlmax; float erlcapa; float f; if (debug) printf("Telemetry Tx run; next one in %.2f minutes\n", (telemetry_interval/60.0)); // Init these for RF transmission buf[0] = 0x03; // AX.25 Control buf[1] = 0xF0; // AX.25 PID ++telemetry_seq; telemetry_seq %= 1000; for (i = 0; i < ErlangLinesCount; ++i) { struct erlangline *E = ErlangLines[i]; struct aprx_interface *sourceaif = find_interface_by_callsign(E->name); if (!sourceaif || !interface_is_telemetrable(sourceaif)) continue; beaconaddrlen = sprintf(beaconaddr, "%s>%s,TCPIP*", E->name, tocall); // First two bytes of BUF are for AX.25 control+PID fields s = buf+2; s += sprintf(s, "T#%03d,", telemetry_seq); // Raw Rx Erlang - plotting scale factor: 1/200 erlmax = 0; #if (USE_ONE_MINUTE_DATA == 1) // Find busiest 1 minute k = E->e1_cursor; t = E->e1_max; if (t > telemetry_1min_steps) t = telemetry_1min_steps; // Up to 10 of 1 minute samples erlcapa = 1.0 / E->erlang_capa; // 1/capa of 1 minute for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e1_max - 1; if (E->e1[k].bytes_rx > erlmax) erlmax = E->e1[k].bytes_rx; } #else // Find busiest 10 minute k = E->e10_cursor; t = E->e10_max; if (t > telemetry_10min_steps) t = telemetry_10min_steps; // Up to 1 of 10 minute samples erlcapa = 0.1 / E->erlang_capa; // 1/capa of 10 minute for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e10_max - 1; if (E->e10[k].bytes_rx > erlmax) erlmax = E->e10[k].bytes_rx; } #endif f = (200.0 * erlcapa * erlmax); s += sprintf(s, "%.1f,", f); // Raw Tx Erlang - plotting scale factor: 1/200 erlmax = 0; #if (USE_ONE_MINUTE_DATA == 1) // Find busiest 1 minute k = E->e1_cursor; t = E->e1_max; if (t > telemetry_1min_steps) t = telemetry_1min_steps; // Up to 10 of 1 minute samples erlcapa = 1.0 / E->erlang_capa; // 1/capa of 1 minute for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e1_max - 1; if (E->e1[k].bytes_tx > erlmax) erlmax = E->e1[k].bytes_tx; } #else // Find busiest 10 minute k = E->e10_cursor; t = E->e10_max; if (t > telemetry_10min_steps) t = telemetry_10min_steps; // Up to 1 of 10 minute samples erlcapa = 0.1 / E->erlang_capa; // 1/capa of 10 minute for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e10_max - 1; if (E->e10[k].bytes_tx > erlmax) erlmax = E->e10[k].bytes_tx; } #endif f = (200.0 * erlcapa * erlmax); s += sprintf(s, "%.1f,", f); erlmax = 0; #if (USE_ONE_MINUTE_DATA == 1) // Sum of 1 minute packet counts k = E->e1_cursor; t = E->e1_max; if (t > telemetry_1min_steps) t = telemetry_1min_steps; /* Up to 10 of 1 minute samples */ for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e1_max - 1; erlmax += E->e1[k].packets_rx; } #else // Sum of 10 minute packet counts erlmax = 0; k = E->e10_cursor; t = E->e10_max; if (t > telemetry_10min_steps) t = telemetry_10min_steps; // Up to 1 of 10 minute samples for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e10_max - 1; erlmax += E->e10[k].packets_rx; } #endif f = erlmax / telemetry_timescaler; s += sprintf(s, "%.1f,", f); erlmax = 0; #if (USE_ONE_MINUTE_DATA == 1) // Sum of 1 minute packet drop counts k = E->e1_cursor; t = E->e1_max; if (t > telemetry_1min_steps) t = telemetry_1min_steps; /* Up to 10 of 1 minute samples */ for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e1_max - 1; erlmax += E->e1[k].packets_rxdrop; } #else // Sum of 10 minute packet drop counts k = E->e10_cursor; t = E->e10_max; if (t > telemetry_10min_steps) t = telemetry_10min_steps; // Up to 1 of 10 minute samples for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e10_max - 1; erlmax += E->e10[k].packets_rxdrop; } #endif f = erlmax / telemetry_timescaler; s += sprintf(s, "%.1f,", f); erlmax = 0; #if (USE_ONE_MINUTE_DATA == 1) // Sum of 1 minute packet tx counts k = E->e1_cursor; t = E->e1_max; if (t > telemetry_1min_steps) t = telemetry_1min_steps; /* Up to 10 of 1 minute samples */ for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e1_max - 1; erlmax += E->e1[k].packets_tx; } #else // Sum of 10 minute packet tx counts k = E->e10_cursor; t = E->e10_max; if (t > telemetry_10min_steps) t = telemetry_10min_steps; // Up to 1 of 10 minute samples for (j = 0; j < t; ++j) { --k; if (k < 0) k = E->e10_max - 1; erlmax += E->e10[k].packets_tx; } #endif f = erlmax / telemetry_timescaler; s += sprintf(s, "%.1f,", f); /* Tail filler */ s += sprintf(s, "00000000"); // FIXME: flag telemetry? if (debug>2) printf("%s (to is=%d rf=%d) %s\n", beaconaddr, sourceaif->telemeter_to_is, sourceaif->telemeter_to_rf, buf+2); /* _NO_ ending CRLF, the APRSIS subsystem adds it. */ /* Send those (net)beacons.. */ buflen = s - buf; #ifndef DISABLE_IGATE if (sourceaif->telemeter_to_is) { aprsis_queue(beaconaddr, beaconaddrlen, qTYPE_LOCALGEN, aprsis_login, buf+2, buflen-2); } #endif rf_telemetry(sourceaif, beaconaddr, buf, buflen); } ++telemetry_params; } // Telemetry Labels are transmitted separately static void telemetry_labeltx() { int i; char buf[200], *s; int buflen; char beaconaddr[60]; int beaconaddrlen; if (debug) printf("Telemetry LabelTx run; next one in %.2f minutes\n", (telemetry_labelinterval/60.0)); // Init these for RF transmission buf[0] = 0x03; // AX.25 Control buf[1] = 0xF0; // AX.25 PID ++telemetry_seq; telemetry_seq %= 1000; for (i = 0; i < ErlangLinesCount; ++i) { struct erlangline *E = ErlangLines[i]; struct aprx_interface *sourceaif = find_interface_by_callsign(E->name); if (!sourceaif || !interface_is_telemetrable(sourceaif)) continue; beaconaddrlen = sprintf(beaconaddr, "%s>%s,TCPIP*", E->name, tocall); // First two bytes of BUF are for AX.25 control+PID fields /* Send every 5h20m or thereabouts. */ switch (telemetry_labelindex) { case 0: s = buf+2 + sprintf(buf+2, ":%-9s:PARM.Avg 10m,Avg 10m,RxPkts,IGateDropRx,TxPkts", E->name); break; case 1: s = buf+2 + sprintf(buf+2, ":%-9s:UNIT.Rx Erlang,Tx Erlang,count/10m,count/10m,count/10m", E->name); break; case 2: s = buf+2 + sprintf(buf+2, ":%-9s:EQNS.0,0.005,0,0,0.005,0,0,1,0,0,1,0,0,1,0", E->name); break; default: break; } if (debug>2) printf("%s (to is=%d rf=%d) %s\n", beaconaddr, sourceaif->telemeter_to_is, sourceaif->telemeter_to_rf, buf+2); buflen = s - buf; #ifndef DISABLE_IGATE if (sourceaif->telemeter_to_is) { aprsis_queue(beaconaddr, beaconaddrlen, qTYPE_LOCALGEN, aprsis_login, buf+2, buflen-2); } #endif rf_telemetry(sourceaif, beaconaddr, buf, buflen); } ++telemetry_params; // Switch label-index.. ++telemetry_labelindex; if (telemetry_labelindex > 2) telemetry_labelindex = 0; } /* * Transmit telemetry to the RF interface that is being monitored. * Interface 'flags' contain controls on thist. */ static void rf_telemetry(const struct aprx_interface *sourceaif, const char *beaconaddr, const char *buf, const const int buflen) { int i; int t_idx; char *dest; if (rftelemetrycount == 0) return; // Nothing to do! if (sourceaif == NULL) return; // Huh? Unknown source.. if (!sourceaif->telemeter_to_rf) return; // not wanted if (!interface_is_telemetrable(sourceaif)) return; // not possible // The beaconaddr comes in as: // "interfacecall>APRXxx,TCPIP*" dest = strchr(beaconaddr, ','); if (dest != NULL) *dest = 0; dest = strchr(beaconaddr, '>'); if (dest != NULL) *dest++ = 0; if (dest == NULL) { // Impossible -- said she... return; } for (t_idx = 0; t_idx < rftelemetrycount; ++t_idx) { struct rftelemetry *rftlm = rftelemetry[t_idx]; if (rftlm == NULL) break; for (i = 0; i < rftlm->source_count; ++i) { if (rftlm->sources[i] == sourceaif) { // Found telemetry transmitter which wants this source interface_transmit_beacon(rftlm->transmitter, beaconaddr, dest, rftlm->viapath, buf, buflen); } } } } int telemetry_config(struct configfile *cf) { char *name, *param1; char *str = cf->buf; int has_fault = 0; struct aprx_interface *aif = NULL; struct aprx_interface **sources = NULL; int source_count = 0; char *viapath = NULL; while (readconfigline(cf) != NULL) { if (configline_is_comment(cf)) continue; /* Comment line, or empty line */ // It can be severely indented... str = config_SKIPSPACE(cf->buf); name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRLOWER(name); param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (strcmp(name, "") == 0) break; if (strcmp(name, "transmit") == 0 || strcmp(name, "transmitter") == 0) { if (strcasecmp(param1,"$mycall") == 0) param1 = (char*)mycall; aif = find_interface_by_callsign(param1); if (aif != NULL && (!aif->tx_ok)) { aif = NULL; // Not printf("%s:%d ERROR: This transmit interface has no TX-OK TRUE setting: '%s'\n", cf->name, cf->linenum, param1); has_fault = 1; } else if (aif == NULL) { printf("%s:%d ERROR: Unknown interface: '%s'\n", cf->name, cf->linenum, param1); has_fault = 1; } } else if (strcmp(name, "via") == 0) { if (viapath != NULL) { printf("%s:%d ERROR: Double definition of 'via'\n", cf->name, cf->linenum); has_fault = 1; } else if (*param1 == 0) { printf("%s:%d ERROR: 'via' keyword without parameter\n", cf->name, cf->linenum); has_fault = 1; } if (!has_fault) { const char *check; config_STRUPPER(param1); check = tnc2_verify_callsign_format(param1, 0, 1, param1+strlen(param1)); if (check == NULL) { has_fault = 1; printf("%s:%d ERROR: The 'via %s' parameter is not acceptable AX.25 format\n", cf->name, cf->linenum, param1); } } if (!has_fault) { // Save it viapath = strdup(param1); } } else if (strcmp(name, "source") == 0) { struct aprx_interface *source_aif = NULL; if (debug) printf("%s:%d source = '%s'\n", cf->name, cf->linenum, param1); if (strcasecmp(param1,"$mycall") == 0) param1 = (char*)mycall; source_aif = find_interface_by_callsign(param1); if (source_aif == NULL) { has_fault = 1; printf("%s:%d ERROR: Digipeater source '%s' not found\n", cf->name, cf->linenum, param1); } else { // Collect them all... sources = realloc(sources, sizeof(void*)*(source_count+3)); sources[source_count++] = source_aif; sources[source_count+1] = NULL; } if (debug>1) printf(" .. source_aif = %p\n", source_aif); } else { printf("%s:%d ERROR: Unknown block keyword '%s'\n", cf->name, cf->linenum, name); } } if (has_fault) { if (sources != NULL) free(sources); if (viapath != NULL) free(viapath); printf("ERROR: Failures on defining block parameters\n"); printf(" APRS RF-Telemetry will not be activated.\n"); } else { struct rftelemetry *newrf = calloc(1, sizeof(*newrf)); newrf->transmitter = aif; newrf->viapath = viapath; newrf->sources = sources; newrf->source_count = source_count; rftelemetry = realloc(rftelemetry, sizeof(void*)*(rftelemetrycount+2)); rftelemetry[rftelemetrycount++] = newrf; if (debug) printf("Defined to transmitter %s\n", aif ? aif->callsign : "ALL"); } return has_fault; } aprx-2.08.svn593/aprx-rxigate.conf.in0000644000175000017500000000655412306650223016355 0ustar colincolin# # Minimal sample configuration file for the APRX-2 as Rx-only iGate. # # This configuration is structured with Apache HTTPD style tags # which then contain subsystem parameters. # # # For simple case, you need to adjust 4 things: # - Mycall parameter # - Select correct type of interface (ax25-device or serial-device) # - Optionally set a beacon telling where this system is # - Optionally enable digipeater with or without tx-igate # # # Define the parameters in following order: # 1) mycall # 2) ** one # 3) ** one # 4) ** possibly multiple for each of radio receivers # # # Global macro for simplified callsign definition: # Usable for 99+% of cases. # mycall N0CALL-1 #login OTHERCALL-7 # login defaults to $mycall # APRS-IS server name and optional portnumber. # Default port is 14580, and it should be enough for you. server rotate.aprs2.net #server euro.aprs2.net #server asia.aprs2.net #server noam.aprs2.net #server soam.aprs2.net #server aunz.aprs2.net # # Passcode for your callsign # passcode -1 # rflog defines a rotatable file into which all RF-received packets # are logged. The host system can rotate it at any time without # need to signal the aprx that the file has been moved. # # rflog @VARLOG@/aprx-rf.log # aprxlog defines a rotatable file into which most important # events on APRS-IS connection are logged, namely connects and # disconnects. The host system can rotate it at any time without # need to signal the aprx that the file has been moved. # # aprxlog @VARLOG@/aprx.log # *********** Multiple definitions can follow ********* # ax25-device Lists AX.25 ports by their callsigns that in Linux # systems receive APRS packets. If none are defined, # or the system is not Linux, the AX.25 network receiver # is not enabled. Used technologies need at least # Linux kernel 2.4.x # # tx-ok Boolean telling if this device is able to transmit. # # # ax25-device $mycall # #tx-ok false # transmitter enable defaults to false # # # The TNC serial options. Parameters are: # - /dev/ttyUSB1 -- tty device # - 19200 -- baud rate, supported ones are: # 1200, 2400, 4800, 9600, 19200, 38400 # - 8n1 -- 8-bits, no parity, one stop-bit, # no other supported modes # - "KISS" - plain basic KISS mode # - "XORSUM" alias "BPQCRC" - KISS with BPQ "CRC" byte # - "SMACK" alias "CRC16" - KISS with real CRC # - "FLEXNET" - KISS with real CRC # - "TNC2" - TNC2 monitor format # - "DPRS" - DPRS (RX) GW # # # serial-device /dev/ttyUSB0 19200 8n1 KISS # #callsign $mycall # callsign defaults to $mycall # #tx-ok false # transmitter enable defaults to false # # # serial-device /dev/ttyUSB1 19200 8n1 TNC2 # #callsign $mycall # callsign defaults to $mycall # #tx-ok false # TNC2 monitor can not have transmitter # # # serial-device /dev/ttyUSB1 19200 8n1 DPRS # callsign dprsgwcallsign # must define actual callsign # #tx-ok false # DPRS monitor can not do transmit # aprx-2.08.svn593/Makefile0000644000175000017500000001507012313357152014122 0ustar colincolin# # APRX -- 2nd generation receive-only APRS-i-gate with # minimal requirement of esoteric facilities or # libraries of any kind beyond UNIX system libc. # # Note: This makefile uses features from GNU make # -------------------------------------------------------------------- # # target paths VARRUN= /var/run # directory for aprx.state and pid-file VARLOG= /var/log/aprx # directory for direct logfiles CFGFILE= /etc/aprx.conf # default configuration file SBINDIR= /usr/sbin # installation path for programs MANDIR= /usr/share/man # installation path for manual pages # -------------------------------------------------------------------- # srcdir = . PROF= # used by 'make profile' # Compiler and flags CC= gcc CFLAGS= -Wall -g -O2 -pthread # Linker and flags LD= gcc LDFLAGS= -Wall -g -O2 -z noexecstack $(PROF) datarootdir= ${prefix}/share INSTALL= $(srcdir)/install-sh INSTALL_PROGRAM=$(INSTALL) -m 755 INSTALL_DATA= $(INSTALL) -m 644 # -------------------------------------------------------------------- # # no user serviceable parts below # -------------------------------------------------------------------- # # strip extra whitespace from paths VARRUN:=$(strip $(VARRUN)) VARLOG:=$(strip $(VARLOG)) CFGFILE:=$(strip $(CFGFILE)) SBINDIR:=$(strip $(SBINDIR)) MANDIR:=$(strip $(MANDIR)) # generate version strings VERSION = 2.08 SVNVERSION = $(shell cat SVNVERSION) versionupdate := $(shell if [ "$(PKG_REV)-$(PKG_RELEASE)" != "-" ]; then echo "$(PKG_REV)-$(PKG_RELEASE)" > SVNVERSION; fi) # VERSION:=$(shell cat VERSION) # SVNVERSION_CMD:=$(shell which svnversion) # SVNVERSION:=$(shell if ${SVNVERSION_CMD} > /dev/null 2>&1 \&\& test -x ${SVNVERSION_CMD} -a \( -d .svn -o -d ../.svn -o -d ../../.svn \) ; then ${SVNVERSION_CMD} | tee SVNVERSION ; else cat SVNVERSION; fi) DATE:=$(shell date +"%Y %B %d") RFCDATE:=$(shell date +"%a, %d %b %Y %H:%M:%S %z") DEFS= -DAPRXVERSION="\"2.08r$(SVNVERSION)\"" \ -DVARRUN="\"$(VARRUN)\"" -DVARLOG="\"$(VARLOG)\"" \ -DCFGFILE="\"$(CFGFILE)\"" # program names PROGAPRX= aprx PROGSTAT= $(PROGAPRX)-stat LIBS= -lrt -lutil -lm -pthread -lrt OBJSAPRX= aprx.o ttyreader.o ax25.o aprsis.o beacon.o config.o \ netax25.o erlang.o aprxpolls.o telemetry.o igate.o \ cellmalloc.o historydb.o keyhash.o parse_aprs.o \ dupecheck.o kiss.o interface.o pbuf.o digipeater.o \ valgrind.o filter.o dprsgw.o crc.o agwpesocket.o \ netresolver.o timercmp.o #ssl.o OBJSSTAT= erlang.o aprx-stat.o aprxpolls.o valgrind.o timercmp.o # man page sources, will be installed as $(PROGAPRX).8 / $(PROGSTAT).8 MANAPRX := aprx.8 MANSTAT := aprx-stat.8 OBJS= $(OBJSAPRX) $(OBJSSTAT) MAN= $(MANAPRX) $(MANSTAT) # -------------------------------------------------------------------- # .PHONY: all all: $(PROGAPRX) $(PROGSTAT) man aprx.conf aprx-complex.conf valgrind: @echo "Did you do 'make clean' before 'make valgrind' ?" make all CFLAGS="${CFLAGS} -D_FOR_VALGRIND_" profile: @echo "Did you do 'make clean' before 'make profile' ?" make all PROF="-pg" $(PROGAPRX): $(OBJSAPRX) VERSION Makefile $(LD) $(LDFLAGS) -o $@ $(OBJSAPRX) $(LIBS) $(PROGSTAT): $(OBJSSTAT) VERSION Makefile $(LD) $(LDFLAGS) -o $@ $(OBJSSTAT) $(LIBS) .PHONY: man man: $(MAN) .PHONY: doc html pdf doc: html pdf pdf: $(MAN:=.pdf) html: $(MAN:=.html) # -------------------------------------------------------------------- # .PHONY: install install-deb install: all $(INSTALL_PROGRAM) $(PROGAPRX) $(DESTDIR)$(SBINDIR)/$(PROGAPRX) $(INSTALL_PROGRAM) $(PROGSTAT) $(DESTDIR)$(SBINDIR)/$(PROGSTAT) $(INSTALL_DATA) $(MANAPRX) $(DESTDIR)$(MANDIR)/man8/$(PROGAPRX).8 $(INSTALL_DATA) $(MANSTAT) $(DESTDIR)$(MANDIR)/man8/$(PROGSTAT).8 if [ ! -f $(DESTDIR)$(CFGFILE) ] ; then \ $(INSTALL_DATA) aprx.conf $(DESTDIR)$(CFGFILE) ; \ else true ; fi .PHONY: clean clean: rm -f $(PROGAPRX) $(PROGSTAT) rm -f $(MAN) $(MAN:=.html) $(MAN:=.ps) $(MAN:=.pdf) \ rm -f aprx.conf logrotate.aprx rm -f *~ *.o *.d .PHONY: distclean distclean: clean rm -f config.log config.status config.h rm -rf autom4te.cache *.log* doc/.~*# # -------------------------------------------------------------------- # %.o: %.c VERSION Makefile $(CC) $(CFLAGS) $(PROF) $(DEFS) -c $< @$(CC) -MM $(CFLAGS) $(PROF) $(DEFS) $< > $(@:.o=.d) $(MAN:=.html): %.html : % sh man-to-html.sh $< > $@ $(MAN:=.ps): %.ps : % groff -man $< > $@ $(MAN:=.pdf): %.pdf : %.ps ps2pdf $< logrotate.aprx $(MAN) aprx-complex.conf aprx.conf: % : %.in VERSION Makefile perl -ne "s{\@DATEVERSION\@}{$(VERSION) - $(DATE)}g; \ s{\@VARRUN\@}{$(VARRUN)}g; \ s{\@VARLOG\@}{$(VARLOG)}g; \ s{\@CFGFILE\@}{$(CFGFILE)}g; \ print;" \ < $< > $@ # -------------------------------------------------------------------- # # # Following is for the original author only... # DISTVERSION:=aprx-$(VERSION).svn$(SVNVERSION) DISTTARGET:=../$(DISTVERSION) RPMVERSION:=$(shell echo "${DISTVERSION}" | sed -e 's/aprx-//') .PHONY: dist svnversion-test svnversion-test: # Special for the source maintainer only.. @sh svnversion-test.sh $(SVNVERSION) dist: svnversion-test if [ ! -d $(DISTTARGET) ] ; then \ mkdir $(DISTTARGET) ; \ fi tar cf - --exclude-backups --exclude-vcs --exclude=windows --exclude=*.log* --exclude=*.conf . | (cd $(DISTTARGET) ; tar xf -) echo "$(DISTVERSION)" > $(DISTTARGET)/VERSION perl -ne "\$$ver = '$(DISTVERSION)'; \ \$$ver =~ tr/0-9.//cd; \ \$$ver .= '-1'; \ s{\@VERSION\@}{\$$ver}g; \ s{\@RFCDATE\@}{$(RFCDATE)}g; \ print;" \ < $(DISTTARGET)/debian/changelog.release \ > $(DISTTARGET)/debian/changelog rm -f $(DISTTARGET)/debian/changelog.release rm -f $(DISTTARGET)/aprx.spec perl -ne "s{\@VERSION\@}{$(RPMVERSION)}g; \ s{\@DATE0\@}{$(DATE0)}g; \ print;" \ < $(DISTTARGET)/rpm/aprx.spec.in \ > $(DISTTARGET)/aprx.spec rm -f $(DISTTARGET)/rpm/aprx.spec.in make -C $(DISTTARGET) distclean cd .. && \ tar czvf $(DISTVERSION).tar.gz $(DISTVERSION) # -------------------------------------------------------------------- # .PHONY: make-deb make-rpm make-deb: if [ -f debian/changelog.release ] ; then \ perl -ne "\$$ver = '$(DISTVERSION)'; \ \$$ver =~ tr/0-9.//cd; \ \$$ver .= '-1'; \ s{\@VERSION\@}{\$$ver}g; \ s{\@RFCDATE\@}{$(RFCDATE)}g; \ print;" \ < debian/changelog.release \ > debian/changelog ; \ fi dpkg-buildpackage -b -us -uc -rfakeroot make-rpm: # actually just a reminder of how to do it.. rpmbuild --target i386 -ta ../$(DISTVERSION).tar.gz # -------------------------------------------------------------------- # # include object depencies if available -include $(OBJS:.o=.d) aprx-2.08.svn593/svnversion0000755000175000017500000000002412067427755014632 0ustar colincolin#!/bin/sh echo 523 aprx-2.08.svn593/configure.in0000644000175000017500000002443012314016324014765 0ustar colincolindnl Process this file with autoconf to produce a configure script. AC_INIT AC_CONFIG_SRCDIR([aprx.h]) dnl For automake VERSION="`cat VERSION`" PACKAGE=aprx dnl AM_INIT_AUTOMAKE($PACKAGE, $VERSION) AC_PROG_MAKE_SET AC_CONFIG_HEADERS([config.h]) dnl Checks for programs. AC_PROG_CC AC_PROG_GCC_TRADITIONAL dnl AC_PATH_PROG(LD, ld, ld)dnl if test -z "$LD" ; then LD="$CC" fi AC_SUBST(LD,"$LD") dnl If on i686, we'll need -march=i686 to get the atomic instructions dnl On FreeBSD, the architecture is i386. MACHINE="`uname -m`" if test "$MACHINE" == "i686" -o "$MACHINE" == "i386"; then CFLAGS_ARCH="-march=i686" fi OS="`uname`" if test "$OS" == "Darwin"; then CFLAGS_ARCH="" fi AC_SUBST(CFLAGS_ARCH) dnl Check for GNU make AX_CHECK_GNU_MAKE() dnl Check for headers. AC_CHECK_HEADERS(time.h sys/time.h stdlib.h stddef.h stdint.h) AC_CHECK_HEADERS(string.h strings.h) AC_CHECK_HEADERS(pty.h) AC_CHECK_HEADERS(pthread.h) dnl Checks for system headers AC_CHECK_HEADERS([alloca.h], AC_DEFINE([HAVE_ALLOCA_H])) AC_CHECK_HEADERS([poll.h], AC_DEFINE([HAVE_POLL_H])) dnl AC_CHECK_FUNC(ppoll,,[Probably have ppoll of Linux]) AC_CHECK_HEADERS([sys/epoll.h], AC_DEFINE([HAVE_SYS_EPOLL_H])) dnl SCTP checks AC_CHECK_HEADERS([netinet/sctp.h], AC_DEFINE([HAVE_NETINET_SCTP_H])) dnl Check for varargs AC_CHECK_HEADERS(stdarg.h varargs.h sys/wait.h) AC_FUNC_VPRINTF dnl This group must be after header tests AC_MSG_RESULT([** Using C compiler: $CC]) AC_MSG_RESULT([** Using CFLAGS: $CFLAGS]) AC_MSG_RESULT([** Using CPPDEP: $CPPDEP]) AC_C_BIGENDIAN dnl AC_INLINE AC_CHECK_SIZEOF(void *) AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(double) dnl AC_DEFINE_UNQUOTED(CONFIGURE_CMD,"$0 $ac_configure_args") AC_DEFINE_UNQUOTED(CONFIGURE_CMD, "CC='$CC' CFLAGS='$CFLAGS' $0 $ac_configure_args", [Configuration command line]) dnl Checks for libraries. AC_SUBST(LIBM) AC_CHECK_FUNCS(atan2f,, AC_CHECK_LIB(m, atan2f, [LIBM="-lm"])) AC_CHECK_FUNCS(memchr memrchr gettimeofday) dnl Checks for library functions. AC_CHECK_FUNCS(openpty,, AC_CHECK_LIB(util, openpty, [AC_DEFINE(HAVE_OPENPTY,1,[])] [LIBS="$LIBS -lutil"])) dnl Check for user options dnl The "EMBEDDED" is now always on, replaced with --with-erlangstorage AC_DEFINE(EMBEDDED,1,[Define for an embedded target]) AC_ARG_WITH(embedded, [ --with-embedded When desiring to target as embedded], [AC_DEFINE(EMBEDDED,1,[Define for an embedded target]) EMBEDDED=1]) AC_ARG_WITH(erlangstorage, [ --with-erlangstorage When desiring a longer term backing storage on erlang datasets. NOT compatible with EMBEDDED, REQUIRES FILESYSTEM!], [AC_DEFINE(ERLANGSTORAGE,1,[Define for a non-embedded system with filesystem based Erlang history storage]) ERLANGSTORAGE=1]) AC_ARG_ENABLE(igate, [ --disable-igate Disable all IGate codes], [if test "${enable_igate}" = no ; then AC_DEFINE(DISABLE_IGATE,1,[Define to 1 if you want to disable all IGATE codes.]) fi]) AC_ARG_ENABLE(agwpe, [ --enable-agwpe Enable AGWPE socket interface code.], [if test "${enable_agwpe}" != no ; then AC_DEFINE(ENABLE_AGWPE,1,[Define to 1 if you want to enable AGWPE socket interface.]) fi]) AC_ARG_WITH(pthread, [ --without-pthread When desiring not to use pthread subsystem], [AC_DEFINE(DISABLE_PTHREAD,1,[Define for pthread(3p) disabling]) DISABLE_PTHREAD=1], [AC_DEFINE(ENABLE_PTHREAD,1,[Define for pthread(3p) enabling]) ENABLE_PTHREAD=1]) AC_ARG_WITH(pthreads, [ --with-pthreads (mistyped pthread) When desiring use pthread subsystem], [AC_DEFINE(ENABLE_PTHREAD,1,[Define for pthread(3p) enabling]) ENABLE_PTHREAD=1]) dnl search for pthread libs and compilation option AC_SUBST(LIBPTHREAD) AC_SUBST(CCPTHREAD) if test "${ENABLE_PTHREAD}" = "1" ; then AC_MSG_RESULT([The --without-pthread option is not set, looking for pthread_create() function.]) have_pthread=no if test $have_pthread = no; then t_oldLibs="$LIBS" LIBS="$LIBS -pthread" t_oldCflags="$CFLAGS" CFLAGS="$CFLAGS -pthread" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]],[[pthread_t pt; pthread_attr_t pat; int rc = pthread_create(&pt, &pat, NULL, NULL)]])], [have_pthread=yes;AC_MSG_RESULT([Have pthread_create()])], [AC_MSG_RESULT([Not have pthread_create()])]) if test $have_pthread = no ; then AC_MSG_RESULT([Failure at HAVE_PTHREAD_CREATE]) else AC_DEFINE(HAVE_PTHREAD_CREATE,1,[Have pthread_create() function]) AC_MSG_RESULT([Success at HAVE_PTHREAD_CREATE]) LIBPTHREAD="-pthread" CCPTHREAD="-pthread" fi LIBS="$t_oldLibs" CFLAGS="$t_oldCflags" fi if test $have_pthread = no; then AC_MSG_RESULT([Failure at HAVE_PTHREAD_CREATE, trying second way.]) t_oldLibs="$LIBS" LIBS="$LIBS -lpthread" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]],[[pthread_t pt; pthread_attr_t pat; int rc = pthread_create(&pt, &pat, NULL, NULL)]])], [have_pthread=yes;AC_MSG_RESULT([Have pthread_create()])], [AC_MSG_RESULT([Not have pthread_create()])]) if test $have_pthread = no ; then AC_MSG_RESULT([Failure at HAVE_PTHREAD_CREATE]) else AC_DEFINE(HAVE_PTHREAD_CREATE,1,[Have pthread_create() function]) AC_MSG_RESULT([Success at HAVE_PTHREAD_CREATE]) LIBPTHREAD="-lpthread" CCPTHREAD="" fi LIBS="$t_oldLibs" CFLAGS="$t_oldCflags" fi if test $have_pthread = no; then AC_MSG_RESULT([Still failure at HAVE_PTHREAD_CREATE, Run out of ways to set it up.]) fi fi dnl Check for clock_gettime, and a library to have it AC_SUBST(LIBRT) AC_SEARCH_LIBS([clock_gettime], [rt], [ AC_DEFINE(HAVE_CLOCK_GETTIME, 1,[Have clock_gettime]) ]) if test "$ac_cv_search_clock_gettime" = "-lrt"; then LIBRT="-lrt" fi dnl Solaris resolver solution: AC_SUBST(LIBGETADDRINFO) AC_CHECK_FUNCS(getaddrinfo,, AC_CHECK_LIB(nsl, getaddrinfo, [LIBGETADDRINFO="-lnsl"])) AC_SUBST(LIBSOCKET)dnl AC_SUBST(LIBRESOLV)dnl # # We check for various libraries # - SysVr4 style of "-lsocket" at first (unless in libc) # The hallmark is connect() routine (we presume) # ac_cv_libsocket_both=1 AC_CHECK_FUNC(connect, ac_cv_libsocket_both=0) AC_CHECK_FUNC(gethostbyname, ac_cv_libsocket_both=0) if test "$ac_cv_libsocket_both" = 1 ; then # Check cache if test "$ac_cv_func_socket_lxnet" = yes ; then AC_MSG_RESULT([need -lxnet library (cached)]) LIBSOCKET="-lnsl -lsocket -lxnet" else if test "$ac_cv_func_socket_lsocket" = yes ; then AC_MSG_RESULT([need -lsocket library (cached)]) LIBSOCKET="-lsocket" if test "$ac_cv_func_gethostbyname_lnsl" = yes ; then LIBSOCKET="-lnsl -lsocket" fi else # Well, will this work ? SysVR4, but not Sun Solaris ? AC_CHECK_LIB(xnet, connect, [LIBSOCKET="-lnsl -lsocket -lxnet" ac_cv_func_socket_lsocket=no ac_cv_func_socket_lxnet=yes],[ AC_CHECK_LIB(socket, connect, [LIBSOCKET="-lsocket" ac_cv_func_socket_lsocket=yes], ac_cv_func_socket_lsocket=no) if test "$ac_cv_func_socket_lsocket" = yes ; then t_oldLibs="$LIBS" LIBS="$LIBS -lsocket $LIBRESOLV" AC_TRY_LINK([],[gethostbyname();], ,[ LIBS="$LIBS -lnsl" # Add this Solaris library.. AC_TRY_LINK([],[gethostbyname();],[ LIBSOCKET="-lsocket -lnsl" ac_cv_func_gethostbyname_lnsl=yes ], [ AC_MSG_ERROR([Weird, '$LIBS' not enough to find gethostnyname() ?!]) ]) ]) LIBS="$t_oldLibs" fi ]) fi fi fi if test "x$LIBRESOLV" = "x"; then # Ok, No -lresolv, is this enough for the _res to appear ? t_oldLibs="$LIBS" LIBS="$LIBS $LIBSOCKET" ac_cv_var__res_options=no # This following is for IRIX6.4, and I sincerely hope it # will not fail on other systems... It did! It did! # Many systems don't have idemponent headers, they need specific # includes before latter ones, or the latter ones won't be successful... AC_TRY_LINK([#include #include #include #include #include ], [_res.options = RES_INIT;], ac_cv_var__res_options=yes); if test "$ac_cv_var__res_options" != "yes"; then AC_MSG_RESULT([Can't do without -lresolv to link resolver's _res.options]) LIBS="$LIBS -lresolv" fi LIBS="$t_oldLibs" fi # See about the routines that possibly exist at the libraries.. LIBS="$t_oldLibs $LIBSOCKET" AC_CHECK_FUNCS(socket socketpair) LIBS="$t_oldLibs" if test "$ac_cv_func_socket" = no -a "$LIBSOCKET" != ""; then LIBS="$LIBS $LIBSOCKET" AC_TRY_LINK([],[socket();], ac_cv_func_socket=yes) if test $ac_cv_func_socket = yes; then AC_DEFINE(HAVE_SOCKET) AC_MSG_RESULT([Has socket() when using $LIBSOCKET]) fi LIBS="$t_oldLibs" fi if test "$ac_cv_func_socketpair" = no -a "$LIBSOCKET" != ""; then LIBS="$LIBS $LIBSOCKET" AC_TRY_LINK([],[socketpair();], ac_cv_func_socketpair=yes) if test $ac_cv_func_socketpair = yes; then AC_DEFINE(HAVE_SOCKETPAIR) AC_MSG_RESULT([Has socketpair() when using $LIBSOCKET]) fi LIBS="$t_oldLibs" fi dnl Check for openssl AC_ARG_WITH(openssl, [ --with-openssl[=DIR] Include OpenSSL support (requires OpenSSL >= 0.9.7)], [with_openssl=$withval], [with_openssl=no]) AC_SUBST(LIBSSL) AC_SUBST(LIBCRYPTO) if test "$with_openssl" != "no" ; then AC_CHECK_HEADERS([openssl/ssl.h]) AC_SEARCH_LIBS(TLSv1_server_method, ssl, AC_DEFINE(HAVE_TLSV1_SERVER_METHOD, 1, [OpenSSL 0.9.7 or later])) AC_SEARCH_LIBS(X509_free, crypto, AC_DEFINE(HAVE_X509_FREE, 1, [OpenSSL 0.9.7 or later])) if test "$ac_cv_search_TLSv1_server_method" = "-lssl"; then LIBSSL="-lssl" fi if test "$ac_cv_search_X509_free" = "-lcrypto"; then LIBCRYPTO="-lcrypto" fi fi dnl Define compilation variables supplying version information t_vers="`cat VERSION`" AC_SUBST(VERSION_STRING, "`cat VERSION`") t_vers="`cat SVNVERSION`" AC_SUBST(SVNVERSION_STRING, "$t_vers") dnl Output files AC_CONFIG_FILES([Makefile]) AC_OUTPUT aprx-2.08.svn593/aprx-complex.conf.in0000644000175000017500000004762312306650223016363 0ustar colincolin# # Sample configuration file for the APRX-2 -- an APRS iGate and Digipeater # # This configuration is structured with Apache HTTPD style tags # which then contain subsystem parameters. # # Define the parameters in following order: # 1) ** zero to many # 2) ** zero or one # 3) ** there can be multiple! # 4) ** zero to many # 5) ** zero to many # 5) ** zero to many (at most one for each Tx) # # # Global macro for simplified callsign definition: # Usable for 99+% of cases. # mycall N0CALL-1 # # Global macro for simplified "my location" definition in # place of explicit "lat nn lon mm" at beacons. Will also # give "my location" reference for "filter m/100". # #myloc lat ddmm.mmN lon dddmm.mmE # Define possibly multiple APRSIS servers, they are connected to # in Round-Robin fashion. There also exist DNS RR servers for # this use, one of them is "rotate.aprsis.net". # The aprsis-login parameter: # Station callsignSSID used for relaying APRS frames into APRS-IS. # #login $mycall # login defaults to $mycall macro # # Passcode for your callsign # passcode -1 # APRS-IS server name and optional portnumber. # # WARNING: Do not change from default port number [14580] # unless you are absolutely certain you want # something else, and you allow that something # else also affect your tx-igate behaviour! # server rotate.aprs2.net #server euro.aprs2.net #server asia.aprs2.net #server noam.aprs2.net #server soam.aprs2.net #server aunz.aprs2.net # Some APRS-IS servers tell every about 20 seconds to all contact # ports that they are there and alive. Others are just silent. # Default value 3*"heartbeat" + some --> 120 (seconds) # #heartbeat-timeout 0 # Disabler in case your server does not do heartbeat #heartbeat-timeout 1m # Interval of one minute (60 seconds) # APRS-IS server may support some filter commands. # See: http://www.aprs-is.net/javAPRSFilter.aspx # # You can define the filter as single long quoted string, or as # many short segments with explaining comments following them. # # Usability of these filters for a Tx-iGate is dubious, but # they exist in case you for example want to Tx-iGate packets # from some source callsigns in all cases even when they are # not in your local area. # #filter "possibly multiple filter specs in quotes" # #filter "m/100" # My-Range filter: positions within 100 km from my location #filter "f/OH2XYZ-3/50" # Friend-Range filter: 50 km of friend's last beacon position # pidfile is UNIX way to tell that others that this program is # running with given process-id number. This has compiled-in # default value of: pidfile @VARRUN@/aprx.pid # pidfile @VARRUN@/aprx.pid # rflog defines a rotatable file into which all RF-received packets # are logged. The host system can rotate it at any time without # need to signal the aprx that the file has been moved. # rflog @VARLOG@/aprx-rf.log # aprxlog defines a rotatable file into which most important # events on APRS-IS connection are logged, namely connects and # disconnects. The host system can rotate it at any time without # need to signal the aprx that the file has been moved. # aprxlog @VARLOG@/aprx.log # dprslog defines a rotatable file into which most important # events on DPRS receiver gateways are logged. # The host system can rotate it at any time without need to # signal the aprx that the file has been moved. # #dprslog @VARLOG@/dprs.log # erlangfile defines a mmap():able binary file, which stores # running sums of interfaces upon which the channel erlang # estimator runs, and collects data. # Depending on the system, it may be running on a filesystem # that actually retains data over reboots, or it may not. # With this backing store, the system does not loose cumulating # erlang data over the current period, if the restart is quick, # and does not stradle any exact minute. # (Do restarts at 15 seconds over an even minute..) # This file is around 0.7 MB per each interface talking APRS. # If this file is not defined or can not be created, internal # non-persistent in-memory storage will be used. # # Built-in default value is: @VARRUN@/aprx.state # #erlangfile @VARRUN@/aprx.state # erlang-loglevel is config file version of the "-l" option # pushing erlang data to syslog(3). # Valid values are (possibly) following: NONE, LOG_DAEMON, # LOG_FTP, LOG_LPR, LOG_MAIL, LOG_NEWS, LOG_USER, LOG_UUCP, # LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, # LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7. If the parameter value is # not acceptable, list of accepted values are printed at startup. # #erlang-loglevel NONE # erlanglog defines a rotatable file into which erlang data # is written in text form. # #erlanglog @VARLOG@/erlang.log # erlang-log1min option logs to syslog/file also 1 minute # interval data from the program. (In addition to 10m and 60m.) # #erlang-log1min # *********** Multiple definitions follow ********* # ax25-device Lists AX.25 ports by their callsigns that in Linux # systems receive APRS packets. If none are defined, # or the system is not Linux, the AX.25 network receiver # is not enabled. Used technologies need at least # Linux kernel 2.4.x # # tx-ok Boolean telling if this device is able to transmit. # # # ax25-device $mycall # tx-ok true # There be transmitter there! # #alias RELAY,WIDE,TRACE # #telem-to-is true # set to 'false' to disable # # The radio serial option. Parameters are: # - /dev/ttyUSB1 -- tty device # - 19200 -- baud rate, supported ones are: # 1200, 2400, 4800, 9600, 19200, 38400 # - 8n1 -- 8-bits, no parity, one stop-bit, # no other supported modes # - KISS/XORSUM/SMACK -- KISS mode variants # TNC2 -- non-KISS text format variant # DPRS -- DPRS (RX) Gateway # # ### KISS mode example with KISS TNC using TNCID 0 # # serial-device /dev/ttyUSB0 19200 8n1 KISS # #alias RELAY,WIDE,TRACE # callsign N0CALL-14 # #telem-to-is true # set to 'false' to disable # ### KISS mode example with multiple sub-interfaces via TNCID multiplexing # # serial-device /dev/ttyUSB0 19200 8n1 KISS # #alias RELAY,WIDE,TRACE # ## kiss-subif 0 == KISS TNCID 0 # # callsign N0CALL-14 # tx-ok true # #telem-to-is true # set to 'false' to disable # # ## kiss-subif 3 == KISS TNCID 3 # # callsign N0CALL-15 # tx-ok false # #telem-to-is true # set to 'false' to disable # # # # serial-device /dev/ttyUSB1 19200 8n1 TNC2 # callsign N0CALL-13 # #telem-to-is true # set to 'false' to disable # # # serial-device /dev/ttyUSB1 19200 8n1 DPRS # callsign dprsgwcallsign # must define actual callsign # #tx-ok false # DPRS monitor can not do transmit # #telem-to-is true # set to 'false' to disable # # # "KISS" - plain basic KISS mode # "XORSUM" alias "BPQCRC" - KISS with BPQ "CRC" byte # "SMACK" alias "CRC16" - KISS with better CRC # "FLEXNET" - KISS with better CRC # "TNC2" - TNC2 monitor format # "DPRS" - DPRS (RX) GW # # Additional/alternate options for the serial-device # # "timeout 15m" sets a timeout monitor (an interval) to make # reopen/reconnect if the serial port/connection to radio # has failed somehow and nothing is heard. Local serial # ports do not in general need this. At APRS silent sites # this may cause repeated reconnects, but it should not # harm either. At busy sites this will handle reconnect # gracefully in case of network failures, and timeout # value can be shortened. # # "" sets optional multiplexer index on KISS type # connections. This id is specific for the multiplexer connection # on given port, and can be in range of 0 thru 7 for SMACK type # links, and up to 15 for KISS, and BPQ type links. # The kiss-subif is settable only for KISS-type connections. # The subif 0 is settable for TNC2 monitor format. # # "callsign NAME" sets callsign used in statistics displays, # and when the message is sent to APRS-IS. # If none are given, then it will use physical port name. # There can be multiple callsign parameters, if each are # prefixed with their own tncid setting. # # "tx-ok true" enables transmit. System will then also require # that used callsign is valid for AX.25. # # "initstring" is of two parts, the keyword, and then a string. # initstring "\xC0\xC0\xFF\xC0\r\nMO 0\rKISS $01\r" # The initstring is a binary string, "\x00" is encodable. # Of the usual C-style codes only "\r" and "\n" are understood. # The initstring is kiss-subif level option. # # # The tcp-device option defines a connection to remote socket # beyond which is a binary transparent connection to a serial # port. The parameter fields: literal IP address (IPv4 or IPv6), # then literal port number, and finally protocol mode. # KISS-protocol parameters are same as with normal serial port. # # # tcp-device 12.34.56.78 4001 KISS # timeout 15m # 15 minutes # #alias RELAY,WIDE,TRACE # # callsign N0CALL-12 # tx-ok false # # # # # tcp-device 12.34.56.78 4002 TNC2 # timeout 5m # 5 minutes # #alias RELAY,WIDE,TRACE # # callsign N0CALL-12 # tx-ok false # # # *********** Multiple definitions follow ********* # # Beacons are sent out to radio transmitters AND/OR APRSIS. # Default is "both", other modes are settable. # #beaconmode { aprsis | both | radio } # # Beacons are sent from a circullar transmission queue, total cycle time # of that queue is 20 minutes by default, and beacons are "evenly" # distributed along it. Actual intervals are randomized to be anything # in between 80% and 100% of the cycle-size / number-of-beacons. # First beacon is sent out 30 seconds after system start. # Tune the cycle-size to be suitable to your number of defined beacons. # #cycle-size 20m # # # Basic beaconed thing is positional message of type "!": # #beacon symbol "R&" lat "0000.00N" lon "00000.00E" comment "Rx-only iGate" #beacon symbol "R&" $myloc comment "Rx-only iGate" # # Following are basic options: # 'symbol' no default, must be defined! # 'lat' coordinate latitude: ddmm.mmN (no default!) # 'lon' coordinate longitude: dddmm.mmE (no default!) # '$myloc' coordinate values taken from global 'myloc' entry, # and usable in place of explicit 'lat'+'lon'. # 'comment' optional tail part of the item, default is nothing # # Sample symbols: # R& is for "Rx-only iGate" # I& is for "Tx-iGate" # /# is for "Digipeater" # I# is for "Tx-iGate + Digipeater"" # # Additional options are: # 'srccall' parameter sets claimed origination address. # 'dstcall' sets destination address, default "APRXnn" # 'interface' parameter picks an interface (must be "tx-ok true" type) # 'via' sets radio distribution pattern, default: none. # 'timefix' On APRS messages with HMS timestamp (hour:min:sec), the # system fixes appropriate field with transmit time timestamp. # # Message type is by default '!', which is positional no timestamp format. # Other possible formats are definable with options: # 'type' Single character setting type: ! = / @, default: ! # 'item' Defines a name of Item (')') type beacons. # 'object' Defines a name of Object (';') type beacons. # # 'file' option tells a file at which a _raw_ APRS message content is # expected to be found as first line of text. Line ending newline # is removed, and no escapes are supported. The timefix is # available, though probably should not be used. # No \-processing is done on read text line. # # 'exec' option tells a computer program which returns to stdout _raw_ APRS # message content without newline. The timefix is # available, though probably should not be used. # No \-processing is done on read text line. # 'timeout' defines number of seconds the exec:ed program has to produce # a single text line of APRS data + ending newline, until it is # considered overdue and will be killed + processing moves to next # beacon item. # # The parameter sets can vary: # a) 'srccall nnn-n dstcall "string" symbol "R&" lat "ddmm.mmN" lon "dddmm.mmE" [comment "any text"] # b) 'srccall nnn-n dstcall "string" symbol "R&" $myloc [comment "any text"] # c) 'srccall nnn-n dstcall "string" raw "string"' # # The a) form flags on some of possible syntax errors in parameters. # It will also create only "!" type messages. The dest parameter # defaults to "APRS", but can be used to give other destinations. # The via parameter can be used to add other keywords, like "NOGATE". # # Writing correct RAW format beacon message is very hard, # which is evidenced by the frequency of bad syntax texts # people so often put there... If you can not be persuaded # not to do it, then at least VERIFY the beacon result on # web service like findu.com, or aprs.fi # # Do remember that the \ -character has special treatment in the # Aprx configuration parser. If you want a '\' on APRS content, # then you encode it on configuration file as: '\\' # # Stranger combinations with explicite "transmit this to interface X": # # #beacon file /tmp/wxbeacon.txt #beacon interface N0CALL-3 srccall N0CALL-3 \ # raw "!0000.00NR00000.00E&aprx - an Rx-only iGate" #beacon interface $mycall symbol "R&" lat "0000.00N" lon "00000.00E" \ # comment "aprx - an Rx-only iGate" #beacon interface $mycall symbol "R&" $myloc \ # comment "aprx - an Rx-only iGate" #beacon interface $mycall symbol "I&" $myloc \ # comment "Tx-iGate" # # *********** definition(s) follow ********* # # The system will always send telemetry for all of its interfaces # to APRSIS, but there is an option to define telemetry to be sent # to radio channel by using following sections for each transmitter # that is wanted to send out the telemetry. # # transmitter - callsign referring to # via - optional via-path, only 1 callsign! # source - one or more of callsigns for which # the telemetry transmission is wanted for # # # transmitter $mycall # via TRACE1-1 # source $mycall # # ## FIXME: to be written # AX.25 filters block selected messages matching on selected regular # expressions. The expressions are case sensitive, and AX.25 address # elements are in all uppercase text. There can be unlimited number # of patterns, type fields are four: "source", "destination", "via", # and "data". These patterns can be used in addition to built-in # hard-coded reject rules listed in documentation. # # ax25-reject-filter source "^NOCALL" # ax25-reject-filter destination "^NOCALL" # ax25-reject-filter via "^NOGATE" # ax25-reject-filter data "^\\?" # # Source interfaces from which the IGATE functionality feeds the data out # By default it feeds from all configured s. # # source IFCALL-1 # source IFCALL-2 # # *********** Multiple definitions follow ********* # The digipeater definitions tell transmitters that receive # AX.25 packets from possibly multiple sources, and then what # to do on the AX.25 headers of those messages. # # There is one transmitter per digipeater -- and inversely, there # can be at most one digipeater for each transmitter. # # In each digipeater there is at least one , usually same # as the transmitter. You may use same on multiple # s. Using multiple instances of same on # a single does not crash the system, but it can cause # packet duplication in case of non-APRS protocols (like AX.25 CONS) # # Use only at most two levels of viscous-delay in your . # Immediate sending is by "0", and a delayed sending is any value # from 1 to 9. This system does not correctly support other than # immediate sending and one level of delay. # # Note: In order to igate correct when multiple receivers and # transmitters are used on single channel, the # definitions of each radio port must have associated # "igate-group N" parameter which has N of value 1 to 3. # See the aprx-manual.pdf for details. # (Default software compilation allows you to have up to # three channels of APRS operation.) # # # # transmitter TXCALL-1 # #ratelimit 60 120 # default: average 60 packets/minute, # # # burst max 120 packets/minute # #srcratelimit 10 20 # Example: by sourcecall: # # average 10 packets/minute, # # burst max 20 packets/minute # # # # # maxreq 4 # # maxdone 4 # # keys TRACE,WIDE,RELAY # # # # # # maxreq 4 # # maxdone 4 # # keys WIDE # # # # # interface TXCALL-1 # # ratelimit 60 120 # default: average 60 packets/minute, # # # burst max 120 packets/minute # # #relay-type digipeat # default mode is "digipeat" # # viscous-delay 0 # default: no viscous delay # # #regex-filter source "RE-pattern" # can define multiple patterns # # #regex-filter destination "RE-pattern" # --"--; generic # # #regex-filter via "RE-pattern" # --"--; generic VIA # # #regex-filter data "RE-pattern" # --"--; APRS payload # # ##filter "javAPRSSrvr adjunct filters" # # # # # Extra receiver(s) # # interface RXCALL-1 # # ratelimit 60 120 # default: average 60 packets/minute, # # # burst max 120 packets/minute # # #relay-type digipeat # default mode is "digipeat" # # #regex-filter source "RE-pattern" # can define multiple patterns # # #regex-filter destination "RE-pattern" # --"--; generic # # #regex-filter via "RE-pattern" # --"--; generic VIA # # #regex-filter data "RE-pattern" # --"--; APRS payload # # ##filter "javAPRSSrvr adjunct filters" # # # # # # APRSIS source makes this tx-igate # # interface APRSIS # # ratelimit 60 120 # default: average 60 packets/minute, # # # burst max 120 packets/minute # # relay-type third-party # Must define this for APRSIS source! # # viscous-delay 5 # recommendation: 5 seconds delay to give # # # RF delivery time make itself known. # # #via-path WIDE2-2 # You can define a via-path for this source # # # # # # DPRS source makes this DPRS->APRS RF gate # # interface DPRS # # ratelimit 60 120 # default: average 60 packets/minute, # # # burst max 120 packets/minute # # relay-type third-party # Must define this for DPRS source! # # #viscous-delay 5 # recommendation: 5 seconds delay to give # # # RF delivery time make itself known. # # #via-path WIDE2-2 # You can define a via-path for this source # # # # aprx-2.08.svn593/crc.c0000644000175000017500000002624112305424764013404 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" /* 3 different CRC algorithms: 1) CRC-16 2) CRC-CCITT 3) CRC-FLEXNET - - - - - - - - - SYMEK et al. have defined a way to run CRC inside KISS frames to verify that the KISS-frame itself is correct: http://www.symek.com/g/smack.html http://www.ir3ip.net/iw3fqg/doc/smak.htm SMACK variation recycles the top-most bit of the TNC-id nibble, and thus permits up to 8 TNC ports on line. Top-most bit is always one on SMACK frames. SMACK runs CRC16 over whole KISS frame buffer, including the CMD byte. The CRC-code is thus _different_ from what will be sent out on radio, the latter being CRC-CCITT (see further below): Following CRC16-polynome is used: X^16 + X^15 + X^2 + 1 The CRC-generator is preset to zero. Chosen initialize to zero does mean that after a correct packet with a correct checksum is ran thru this CRC, the output checksum will be zero. - - - - - - - - - -- ITU-T V.42 / 1993: 8.1.1.6.1 16-bit frame check sequence The FCS field shall be the sixteen-bit sequence preceding the closing flag. The 16-bit FCS shall be the ones complement of the sum (modulo 2) of a) the remainder of x^k (x^15 + x^14 + x^13 + x^12 + x^11 + x^10 + x^9 + x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x^1 + 1) divided (modulo 2) by the generator polynomial x^16 + x^12 + x^5 + 1, where k is the number of bits in the frame existing between, but not including, the final bit of the opening flag and the first bit of the FCS, excluding bits inserted for transparency; and b) the remainder of the division (modulo 2) by the generator polynomial x^16 + x^12 + x^5 + 1, of the product of x^16 by the content of the frame existing between, but not including, the final bit of the opening flag and the first bit of the FCS, excluding bits inserted for transparency. As a typical implementation at the transmitter, the initial content of the register of the device computing the remainder of the division is preset to all 1s and is then modified by division by the generator polynomial (as described above) of the address, control and information fields; the ones complement of the resulting remainder is transmitted as the sixteen-bit FCS. As a typical implementation at the receiver, the initial content of the register of the device computing the remainder is preset to all 1s. The final remainder, after multiplication by x^16 and then division (modulo 2) by the generator polynomial x^16 + x^12 + x^5 + 1 of the serial incoming protected bits and the FCS, will be “0001 1101 0000 1111” (x^15 through x^0, respectively) in the absence of transmission errors. Same wording is also on ITU-T X.25 specification. - - - - - - - - - Where is FLEXNET CRC specification? */ // Polynome 0xA001 // referred from kiss.c ! const uint16_t crc16_table[] = { 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 }; uint16_t calc_crc_16(const uint8_t *buf, int n) { uint16_t crc = 0; while (--n >= 0) { crc = (((crc >> 8) & 0xff) ^ crc16_table[(crc ^ *buf++) & 0xFF]); } return crc; } // Return 0 for correct result, anything else for incorrect one int check_crc_16(const uint8_t *buf, int n) { uint16_t crc = calc_crc_16(buf, n); return (crc != 0); // Correct result is when crc == 0 } // Polynome 0x8408 const uint16_t crc_ccitt_table[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; uint16_t calc_crc_ccitt(uint16_t crc, const uint8_t *buffer, int len) { while (len--) { uint8_t c = *buffer++; crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ c) & 0xff]; } return crc; } #if 0 // not used! // From start of packet to end of packet _including_ 16 bits of FCS // Return 0 for OK, other values for errors int check_crc_ccitt(const uint8_t *buf, int n) { uint16_t crc = calc_crc_ccitt(0xFFFF, buf, n); return (crc != 0xF0B8); } #endif const uint16_t crc_flex_table[] = { 0x0f87, 0x1e0e, 0x2c95, 0x3d1c, 0x49a3, 0x582a, 0x6ab1, 0x7b38, 0x83cf, 0x9246, 0xa0dd, 0xb154, 0xc5eb, 0xd462, 0xe6f9, 0xf770, 0x1f06, 0x0e8f, 0x3c14, 0x2d9d, 0x5922, 0x48ab, 0x7a30, 0x6bb9, 0x934e, 0x82c7, 0xb05c, 0xa1d5, 0xd56a, 0xc4e3, 0xf678, 0xe7f1, 0x2e85, 0x3f0c, 0x0d97, 0x1c1e, 0x68a1, 0x7928, 0x4bb3, 0x5a3a, 0xa2cd, 0xb344, 0x81df, 0x9056, 0xe4e9, 0xf560, 0xc7fb, 0xd672, 0x3e04, 0x2f8d, 0x1d16, 0x0c9f, 0x7820, 0x69a9, 0x5b32, 0x4abb, 0xb24c, 0xa3c5, 0x915e, 0x80d7, 0xf468, 0xe5e1, 0xd77a, 0xc6f3, 0x4d83, 0x5c0a, 0x6e91, 0x7f18, 0x0ba7, 0x1a2e, 0x28b5, 0x393c, 0xc1cb, 0xd042, 0xe2d9, 0xf350, 0x87ef, 0x9666, 0xa4fd, 0xb574, 0x5d02, 0x4c8b, 0x7e10, 0x6f99, 0x1b26, 0x0aaf, 0x3834, 0x29bd, 0xd14a, 0xc0c3, 0xf258, 0xe3d1, 0x976e, 0x86e7, 0xb47c, 0xa5f5, 0x6c81, 0x7d08, 0x4f93, 0x5e1a, 0x2aa5, 0x3b2c, 0x09b7, 0x183e, 0xe0c9, 0xf140, 0xc3db, 0xd252, 0xa6ed, 0xb764, 0x85ff, 0x9476, 0x7c00, 0x6d89, 0x5f12, 0x4e9b, 0x3a24, 0x2bad, 0x1936, 0x08bf, 0xf048, 0xe1c1, 0xd35a, 0xc2d3, 0xb66c, 0xa7e5, 0x957e, 0x84f7, 0x8b8f, 0x9a06, 0xa89d, 0xb914, 0xcdab, 0xdc22, 0xeeb9, 0xff30, 0x07c7, 0x164e, 0x24d5, 0x355c, 0x41e3, 0x506a, 0x62f1, 0x7378, 0x9b0e, 0x8a87, 0xb81c, 0xa995, 0xdd2a, 0xcca3, 0xfe38, 0xefb1, 0x1746, 0x06cf, 0x3454, 0x25dd, 0x5162, 0x40eb, 0x7270, 0x63f9, 0xaa8d, 0xbb04, 0x899f, 0x9816, 0xeca9, 0xfd20, 0xcfbb, 0xde32, 0x26c5, 0x374c, 0x05d7, 0x145e, 0x60e1, 0x7168, 0x43f3, 0x527a, 0xba0c, 0xab85, 0x991e, 0x8897, 0xfc28, 0xeda1, 0xdf3a, 0xceb3, 0x3644, 0x27cd, 0x1556, 0x04df, 0x7060, 0x61e9, 0x5372, 0x42fb, 0xc98b, 0xd802, 0xea99, 0xfb10, 0x8faf, 0x9e26, 0xacbd, 0xbd34, 0x45c3, 0x544a, 0x66d1, 0x7758, 0x03e7, 0x126e, 0x20f5, 0x317c, 0xd90a, 0xc883, 0xfa18, 0xeb91, 0x9f2e, 0x8ea7, 0xbc3c, 0xadb5, 0x5542, 0x44cb, 0x7650, 0x67d9, 0x1366, 0x02ef, 0x3074, 0x21fd, 0xe889, 0xf900, 0xcb9b, 0xda12, 0xaead, 0xbf24, 0x8dbf, 0x9c36, 0x64c1, 0x7548, 0x47d3, 0x565a, 0x22e5, 0x336c, 0x01f7, 0x107e, 0xf808, 0xe981, 0xdb1a, 0xca93, 0xbe2c, 0xafa5, 0x9d3e, 0x8cb7, 0x7440, 0x65c9, 0x5752, 0x46db, 0x3264, 0x23ed, 0x1176, 0x00ff }; uint16_t calc_crc_flex(const uint8_t *cp, int size) { uint16_t crc = 0xffff; while (size--) { uint8_t c = *cp++; crc = (crc << 8) ^ crc_flex_table[((crc >> 8) ^ c) & 0xff]; } return crc; } #if 0 // not used! int check_crc_flex(const uint8_t *cp, int size) { uint16_t crc = calc_crc_flex(cp, size); if (size < 3) return -1; if (crc != 0x7070) return -1; return 0; } #endif aprx-2.08.svn593/netresolver.c0000644000175000017500000001026312314016324015167 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" #if defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD) #include #include pthread_t netresolv_thread; pthread_attr_t pthr_attrs; #endif static int nrcount; static struct netresolver **nr; static int netresolv_die_now; static int RE_RESOLVE_INTERVAL = 300; // 15 minutes ? struct netresolver *netresolv_add(const char *hostname, const char *port) { struct netresolver *n = malloc(sizeof(*n)); memset(n, 0, sizeof(*n)); n->hostname = hostname; n->port = port; n->ai.ai_addr = &n->sa; ++nrcount; nr = realloc(nr, sizeof(void*)*nrcount); nr[nrcount-1] = n; return n; } static void resolve_all(void) { int i; if (debug>1) printf("netresolve nrcount=%d\n", nrcount); for (i = 0; i < nrcount; ++i) { struct netresolver *n = nr[i]; struct addrinfo *ai, req; int rc; timetick(); if (timecmp(n->re_resolve_time, tick.tv_sec) > 0) { // Not yet to re-resolve this one if (debug>1) printf("nr[%d] re_resolve_time in future (%d secs)\n", i, (int)(n->re_resolve_time - tick.tv_sec)); continue; } memset(&req, 0, sizeof(req)); req.ai_socktype = SOCK_STREAM; req.ai_protocol = IPPROTO_TCP; req.ai_flags = 0; #if 1 req.ai_family = AF_UNSPEC; /* IPv4 and IPv6 are both OK */ #else req.ai_family = AF_INET; /* IPv4 only */ #endif ai = NULL; rc = getaddrinfo(n->hostname, n->port, &req, &ai); if (rc != 0) { // re-resolving failed, discard possible junk result if (debug>1) printf("nr[%d] resolving of %s:%s failed, error: %s\n", i, n->hostname, n->port, gai_strerror(errno)); if (ai != NULL) freeaddrinfo(ai); continue; } if (debug>1) printf("nr[%d] resolving of %s:%s success!\n", i, n->hostname, n->port); timetick(); // Make local static copy of first result memcpy(&n->sa, ai->ai_addr, ai->ai_addrlen); n->ai.ai_flags = ai->ai_flags; n->ai.ai_family = ai->ai_family; n->ai.ai_socktype = ai->ai_socktype; n->ai.ai_protocol = ai->ai_protocol; n->ai.ai_addrlen = ai->ai_addrlen; n->ai.ai_addrlen = ai->ai_addrlen; freeaddrinfo(ai); n->re_resolve_time = tick.tv_sec + RE_RESOLVE_INTERVAL; } } #if defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD) static void netresolv_runthread(void) { sigset_t sigs_to_block; sigemptyset(&sigs_to_block); sigaddset(&sigs_to_block, SIGALRM); sigaddset(&sigs_to_block, SIGINT); sigaddset(&sigs_to_block, SIGTERM); sigaddset(&sigs_to_block, SIGQUIT); sigaddset(&sigs_to_block, SIGHUP); sigaddset(&sigs_to_block, SIGURG); sigaddset(&sigs_to_block, SIGPIPE); sigaddset(&sigs_to_block, SIGUSR1); pthread_sigmask(SIG_BLOCK, &sigs_to_block, NULL); // the main program can cancel us at will pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); while (!die_now) { poll(NULL, 0, 30000); // Sleep 30 seconds (in a reliable way) resolve_all(); } } #endif // Start netresolver thread, but at first run one round of resolving! void netresolv_start(void) { resolve_all(); #if defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD) pthread_attr_init(&pthr_attrs); /* 64 kB stack is enough for this thread (I hope!) default of 2 MB is way too much...*/ pthread_attr_setstacksize(&pthr_attrs, 64*1024); pthread_create(&netresolv_thread, &pthr_attrs, (void*)netresolv_runthread, NULL); #endif } // Shutdown the netresolver thread void netresolv_stop(void) { die_now = 1; #if defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD) pthread_cancel(netresolv_thread); pthread_join(netresolv_thread, NULL); #endif } aprx-2.08.svn593/beacon.c0000644000175000017500000010602212320105344014043 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" struct beaconmsg { time_t nexttime; int interval; const struct aprx_interface *interface; const char *src; const char *dest; const char *via; const char *msg; const char *filename; const char *execfile; int8_t beaconmode; // -1: net only, 0: both, +1: radio only int8_t timefix; int timeout; }; struct beaconset { struct beaconmsg **beacon_msgs; struct timeval beacon_nexttime; float beacon_cycle_size; int beacon_msgs_count; int beacon_msgs_cursor; int exec_pid; int exec_fd; time_t exec_deadline; // seconds char *exec_buf; int exec_buf_length; int exec_buf_space; struct beaconmsg *exec_bm; }; static struct beaconset **bsets; static int bsets_count; static void beacon_it(struct beaconset *bset, struct beaconmsg *bm); static void beacon_reset(struct beaconset *bset) { tv_timeradd_seconds(&bset->beacon_nexttime, &tick, 30); // start 30 seconds from now bset->beacon_msgs_cursor = 0; } static void beacon_set(struct configfile *cf, const char *p1, char *str, const int beaconmode, struct beaconset *bset) { const char *srcaddr = NULL; const char *destaddr = NULL; const char *via = NULL; const char *name = NULL; int buflen = strlen(p1) + strlen(str ? str : "") + 10; char *buf = alloca(buflen); const char *to = NULL; char *code = NULL; const char *lat = NULL; const char *lon = NULL; char *comment = NULL; char *type = NULL; const struct aprx_interface *aif = NULL; int has_fault = 0; struct beaconmsg *bm = calloc(1, sizeof(*bm)); *buf = 0; if (debug) { printf("BEACON parameters: "); } while (*p1) { /* if (debug) printf("p1='%s' ",p1); */ if (strcmp(p1, "interface") == 0 || strcmp(p1, "to") == 0) { if (to != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } to = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (beaconmode < 0) { printf("%s:%d ERROR: beaconmode APRSIS is incompatible with beaconing to designated interface ('%s %s')\n", cf->name, cf->linenum, p1, to); has_fault = 1; goto discard_bm; // sigh.. } if (strcasecmp(to,"$mycall") == 0) { to = mycall; } else { config_STRUPPER((void*)to); } aif = find_interface_by_callsign(to); if ((aif != NULL) && (!aif->tx_ok)) { aif = NULL; // Not an TX interface :-( if (debug)printf("\n"); printf("%s:%d ERROR: beacon interface '%s' that is not a TX capable interface.\n", cf->name, cf->linenum, to); has_fault = 1; goto discard_bm; // sigh.. } else if (aif == NULL) { if (debug)printf("\n"); printf("%s:%d ERROR: beacon interface '%s' that is not a known interface.\n", cf->name, cf->linenum, to); has_fault = 1; } if (debug) printf("interface '%s' ", to); } else if (strcmp(p1, "srccall") == 0 || strcmp(p1, "for") == 0) { if (srcaddr != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } srcaddr = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (strcasecmp(srcaddr,"$mycall") == 0) { srcaddr = mycall; } else { config_STRUPPER((void*)srcaddr); } // What about ITEM and OBJECT ? // if (!validate_callsign_input((char *) srcaddr),1) { // if (debug)printf("\n"); // printf("Invalid rfbeacon FOR callsign"); // } if (debug) printf("srccall '%s' ", srcaddr); } else if (strcmp(p1, "dstcall") == 0 || strcmp(p1, "dest") == 0) { if (destaddr != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } destaddr = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRUPPER((void*)destaddr); if (debug) printf("dstcall '%s' ", destaddr); } else if (strcmp(p1, "via") == 0) { if (via != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } via = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRUPPER((void*)via); if (debug) printf("via '%s' ", via); } else if (strcmp(p1, "name") == 0) { if (name != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (debug) printf("name '%s' ", name); } else if (strcmp(p1, "item") == 0) { if (name != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } if (type != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of type parameter\n", cf->name, cf->linenum); } type = ")"; name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (debug) printf("item '%s' ", name); } else if (strcmp(p1, "object") == 0) { if (name != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } if (type != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of type parameter\n", cf->name, cf->linenum); } type = ";"; name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (debug) printf("object '%s' ", name); } else if (strcmp(p1, "type") == 0) { /* text up to .. 40 chars */ if (type != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } type = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); type = strdup(type); if (debug) printf("type '%s' ", type); if (type[1] != 0 || (type[0] != '!' && type[0] != '=' && type[0] != '/' && type[0] != '@' && type[0] != ';' && type[0] != ')')) { has_fault = 1; printf("%s:%d Sorry, packet constructor's supported APRS packet types are only: ! = / @ ; )\n", cf->name, cf->linenum); } } else if (strcmp(p1, "$myloc") == 0) { if (myloc_latstr != NULL) { lat = myloc_latstr; lon = myloc_lonstr; } else { has_fault = 1; printf("%s:%d ERROR: $myloc has not been defined.\n", cf->name, cf->linenum); } } else if (strcmp(p1, "lat") == 0) { /* ddmm.mmN */ if (lat != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } lat = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (!has_fault && validate_degmin_input(lat, 90)) { has_fault = 1; printf("%s:%d ERROR: Latitude input has bad format: '%s'\n", cf->name, cf->linenum, lat); } if (debug) printf("lat '%s' ", lat); } else if (strcmp(p1, "lon") == 0) { /* dddmm.mmE */ if (lon != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } lon = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (validate_degmin_input(lon, 180)) { has_fault = 1; printf("Longitude input has bad format: '%s'\n",lon); } if (debug) printf("lon '%s' ", lon); } else if (strcmp(p1, "symbol") == 0) { /* R& */ if (code != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } code = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (strlen(code) != 2) { has_fault = 1; printf("Symbol code lenth is not exactly 2 chars\n"); } if (debug) printf("symbol '%s' ", code); } else if (strcmp(p1, "comment") == 0) { /* text up to .. 40 chars */ if (comment != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } comment = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (debug) printf("comment '%s' ", comment); } else if (strcmp(p1, "raw") == 0) { p1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (bm->msg != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } else bm->msg = strdup(p1); // FIXME: validate the data with APRS parser... if (debug) printf("raw '%s' ", bm->msg); } else if (strcmp(p1, "file") == 0) { p1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (bm->filename != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } else bm->filename = strdup(p1); if (debug) printf("file '%s' ", bm->filename); } else if (strcmp(p1, "exec") == 0) { p1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (bm->execfile != NULL) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } else bm->execfile = strdup(p1); // Set default timeout if not yet set. if (bm->timeout == 0) bm->timeout = 10; if (debug) printf("exec file '%s' ", bm->execfile); } else if (strcmp(p1, "timeout") == 0) { p1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); bm->timeout = atoi(p1); if (debug) printf("timeout %d ", bm->timeout); } else if (strcmp(p1, "timefix") == 0) { if (bm->timefix) { has_fault = 1; printf("%s:%d ERROR: Double definition of %s parameter\n", cf->name, cf->linenum, p1); } bm->timefix = 1; if (debug) printf("timefix "); } else { has_fault = 1; #if 0 if (debug) printf("Unknown keyword: '%s'", p1); p1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); #else /* Unknown keyword, a raw message ? */ bm->msg = strdup(p1); if (debug) printf("ASSUMING raw '%s' ", bm->msg); break; #endif } p1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); } if (debug) printf("\n"); if (has_fault) goto discard_bm; if (aif == NULL && beaconmode >= 0) { if (debug) printf("%s:%d Note: Lacking 'interface' keyword for this beacon definition. Beaconing to all Tx capable interfaces + APRSIS (mode depending)\n", cf->name, cf->linenum); } /* if (srcaddr == NULL) srcaddr = mycall; if (srcaddr == NULL) { if (debug) printf("%s:%d Note: Lacking the 'for' keyword for this beacon definition.\n", cf->name, cf->linenum); has_fault = 1; goto discard_bm; } */ if (destaddr == NULL) destaddr = tocall; bm->src = srcaddr != NULL ? strdup(srcaddr) : NULL; bm->dest = strdup(destaddr); bm->via = via != NULL ? strdup(via) : NULL; bm->interface = aif; bm->beaconmode = beaconmode; if (!bm->msg && !bm->filename && !bm->execfile) { /* Not raw packet, perhaps composite ? */ if (!type) type = "!"; if (code && strlen(code) == 2 && lat && strlen(lat) == 8 && lon && strlen(lon) == 9) { if ( strcmp(type,"!") == 0 || strcmp(type,"=") == 0 ) { sprintf(buf, "%s%s%c%s%c%s", type, lat, code[0], lon, code[1], comment ? comment : ""); } else if ( strcmp(type,"/") == 0 || strcmp(type,"@") == 0) { sprintf(buf, "%s111111z%s%c%s%c%s", type, lat, code[0], lon, code[1], comment ? comment : ""); } else if ( strcmp(type,";") == 0 && name) { // Object sprintf(buf, ";%-9.9s*111111z%s%c%s%c%s", name, lat, code[0], lon, code[1], comment ? comment : ""); } else if ( strcmp(type,")") == 0 && name) { // Item sprintf(buf, ")%-3.9s!%s%c%s%c%s", name, lat, code[0], lon, code[1], comment ? comment : ""); } bm->msg = strdup(buf); } else { if (!code || (code && strlen(code) != 2)) printf("%s:%d .. BEACON definition failure; symbol parameter missing or wrong size\n", cf->name, cf->linenum); if (!lat || (lat && strlen(lat) != 8)) printf("%s:%d .. BEACON definition failure; lat(itude) parameter missing or wrong size\n", cf->name, cf->linenum); if (!lon || (lon && strlen(lon) != 9)) printf("%s:%d .. BEACON definition failure; lon(gitude) parameter missing or wrong size\n", cf->name, cf->linenum); /* parse failure, abandon the alloc too */ has_fault = 1; goto discard_bm; } } if (debug) { switch (beaconmode) { case 1: printf("RFONLY"); break; case 0: printf("RF+NET"); break; default: printf("NETONLY"); break; } printf(" BEACON FOR "); if (srcaddr == NULL) printf("***>%s", destaddr); else printf("%s>%s",srcaddr,destaddr); if (via != NULL) printf(",%s", via); if (bm->filename) printf("' file %s\n", bm->filename); else printf("' '%s'\n", bm->msg); } /* realloc() works also when old ptr is NULL */ bset->beacon_msgs = realloc(bset->beacon_msgs, sizeof(bm) * (bset->beacon_msgs_count + 3)); bset->beacon_msgs[bset->beacon_msgs_count++] = bm; bset->beacon_msgs[bset->beacon_msgs_count] = NULL; if (bm->msg != NULL) { // Make this into AX.25 UI frame // with leading control byte.. int len = strlen(bm->msg); char *msg = realloc((void*)bm->msg, len+3); // make room memmove(msg+2, msg, len+1); // move string end \0 also msg[0] = 0x03; // Control byte msg[1] = 0xF0; // PID 0xF0 bm->msg = msg; } beacon_reset(bset); if (0) { discard_bm: if (bm->dest != NULL) free((void*)(bm->dest)); if (bm->msg != NULL) free((void*)(bm->msg)); free(bm); } return; } static void free_beaconmsg(struct beaconmsg *bmsg) { if (bmsg == NULL) return; if (bmsg->src) free((void*)bmsg->src); if (bmsg->dest) free((void*)bmsg->dest); if (bmsg->via) free((void*)bmsg->via); if (bmsg->msg) free((void*)bmsg->msg); if (bmsg->filename) free((void*)bmsg->filename); if (bmsg->execfile) free((void*)bmsg->execfile); free(bmsg); } static void free_beaconset(struct beaconset *bset) { int i; if (bset == NULL) return; for (i = 0; i < bset->beacon_msgs_count; ++i) { free_beaconmsg(bset->beacon_msgs[i]); } free(bset); } int beacon_config(struct configfile *cf) { char *name, *param1; char *str = cf->buf; int beaconmode = 0; int has_fault = 0; struct beaconset *bset = calloc(1, sizeof(*bset)); bset->beacon_cycle_size = 20.0*60.0; // 20 minutes is the default while (readconfigline(cf) != NULL) { if (configline_is_comment(cf)) continue; /* Comment line, or empty line */ // It can be severely indented... str = config_SKIPSPACE(cf->buf); name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRLOWER(name); param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (strcmp(name, "") == 0) break; if (strcmp(name, "cycle-size") == 0) { int v; if (config_parse_interval(param1, &v)) { // Error has_fault = 1; continue; } bset->beacon_cycle_size = (float)v; if (debug) printf("Beacon cycle size: %.2f\n", bset->beacon_cycle_size/60.0); continue; } if (strcmp(name, "beacon") == 0) { beacon_set(cf, param1, str, beaconmode, bset); } else if (strcmp(name, "beaconmode") == 0) { if (strcasecmp(param1, "both") == 0) { beaconmode = 0; } else if (strcasecmp(param1,"radio") == 0) { beaconmode = 1; } else if (strcasecmp(param1,"aprsis") == 0) { beaconmode = -1; } else { printf("%s:%d ERROR: Unknown beaconmode parameter keyword: '%s'\n", cf->name, cf->linenum, param1); has_fault = 1; } } else { printf("%s:%d ERROR: Unknown block config keyword: '%s'\n", cf->name, cf->linenum, name); has_fault = 1; continue; } } if (has_fault) { // discard it.. free_beaconset(bset); } else { // save it.. ++bsets_count; bsets = realloc( bsets,sizeof(*bsets)*bsets_count ); bsets[bsets_count-1] = bset; if (debug > 0) { printf(" set %d defined with %d entries\n", bsets_count, bset->beacon_msgs_count); } } return has_fault; } static void fix_beacon_time(char *txt, int txtlen) { int hour, min, sec; char hms[8]; sec = now.tv_sec % (3600*24); // UNIX time is UTC -> no need to play with fancy timezone conversions and summer times... hour = sec / 3600; min = (sec / 60) % 60; sec = sec % 60; sprintf(hms, "%02d%02d%02dh", hour, min, sec); txt += 2; txtlen -= 2; // Skip Control+PID if (*txt == ';' && txtlen >= 36) { // Object // ;434.775-B*111111z6044.06N/02612.79Er memcpy( txt+11, hms, 7 ); // Overwrite with new time } else if ((*txt == '/' || *txt == '@') && txtlen >= 27) { // Position with timestamp memcpy( txt+1, hms, 7 ); // Overwrite with new time } } static char *msg_read_file(const char *filename, char *buf, int buflen) { FILE *fp = fopen(filename,"r"); if (!fp) return NULL; if (fgets(buf, buflen, fp)) { char *p = strchr(buf, '\n'); if (p) *p = 0; } else { *buf = 0; } fclose(fp); if (*buf == 0) return NULL; return buf; } static void beacon_resettimer(void *arg) { const struct beaconset *bset = (struct beaconset *)arg; float beacon_increment; int i; time_t t = tick.tv_sec; srand((long)t); beacon_increment = (bset->beacon_cycle_size / bset->beacon_msgs_count); if (debug) printf("beacons cycle: %.2f minutes, increment: %.2f minutes\n", bset->beacon_cycle_size/60.0, beacon_increment/60.0); for (i = 0; i < bset->beacon_msgs_count; ++i) { int r = rand() % 1024; int interval = (int)(beacon_increment - 0.2*beacon_increment * (r*0.001)); if (interval < 3) interval = 3; // Minimum interval: 3 seconds t += interval; if (debug) printf("beacons offset: %.2f minutes\n", (t-tick.tv_sec)/60.0); bset->beacon_msgs[i]->nexttime = t; } } static void msg_exec_read(struct beaconset *bset) { int rc; int space = bset->exec_buf_space - bset->exec_buf_length; if (debug) printf("msg_exec_read\n"); if (space < 1) { space += 256; bset->exec_buf_space += 256; bset->exec_buf = realloc(bset->exec_buf, bset->exec_buf_space); } while ((rc = read(bset->exec_fd, bset->exec_buf + bset->exec_buf_length, space)) > 0) { char *p; bset->exec_buf_length += rc; space -= rc; p = memrchr(bset->exec_buf, '\n', bset->exec_buf_length); if (p) { if (debug) printf("found newline in exec read data\n"); *p = 0; bset->exec_buf_length = p - bset->exec_buf; struct beaconmsg *bm = bset->exec_bm; if (bset->exec_buf_length > 2) { // Run that beacon! // Point it to read buffer bm->msg = bset->exec_buf; if (debug) printf(".. calling beacon_it() on buffer: %s\n", bm->msg+2); beacon_it(bset, bm); } else { if (debug) printf(".. nothing read from exec pipe\n"); } // erase the read buffer pointer bm->msg = NULL; // restore the nexttime bset->beacon_nexttime.tv_sec = bm->nexttime; close(bset->exec_fd); bset->exec_fd = -1; //bset->exec_pid = 0; return; } if (debug) printf("no newline in exec read data\n"); if (space < 1) { aprxlog("BEACON EXEC output overflowed read buffer."); rc = 0; // simulate as if.. and kill it. break; } } if (rc == 0) { // EOF read char *p; if (debug) printf("Seen EOF on exec-read\n"); p = memrchr(bset->exec_buf, '\n', bset->exec_buf_length); if (p) { *p = 0; bset->exec_buf_length = p - bset->exec_buf; struct beaconmsg *bm = bset->exec_bm; if (bset->exec_buf_length > 2) { // Run that beacon! // Point it to read buffer bm->msg = bset->exec_buf; if (debug) printf(".. calling beacon_it() on buffer: %s\n", bm->msg+2); beacon_it(bset, bm); } else { if (debug) printf(".. nothing read from exec pipe\n"); } // erase the read buffer pointer bm->msg = NULL; // restore the nexttime bset->beacon_nexttime.tv_sec = bm->nexttime; } else { aprxlog("BEACON EXEC abnormal close."); } close(bset->exec_fd); bset->exec_fd = -1; //bset->exec_pid = 0; } } static int msg_exec_file(const char *filename, int timeout, struct beaconset *bset) { int p[2]; int pid; int dev_null; if (pipe(p)) { return 0; } pid = fork(); if (pid < 0) { close(p[0]); close(p[1]); return 0; } if (pid == 0) { //child if (debug) fprintf(stderr,"execing child pid %d, file: %s\n", getpid(), filename); close(p[0]); if (p[1] != 1) { dup2(p[1], 1); close(p[1]); } dev_null = open("/dev/null", O_WRONLY); if (debug && dev_null < 0) fprintf(stderr,"child process: Failed to open file: /den/null\n"); if (dev_null >= 0) { if (dev_null != 2) { dup2(dev_null, 2); close(dev_null); } dup2(2, 0); } //FIXME: change second parameter execl(filename, "aprx", NULL); if (debug) fprintf(stderr,"child process: Failed to execute: %s\n", filename); exit(255); } // parent bset->exec_deadline = tick.tv_sec + timeout; bset->exec_pid = pid; bset->exec_fd = p[0]; bset->beacon_nexttime.tv_sec = bset->exec_deadline; close(p[1]); return 1; } // int val; // waitpid(pid, &val, 0); // if (WIFEXITED(val) && WEXITSTATUS(val) == 0) { // return buf; // } static void beacon_now(struct beaconset *bset) { struct beaconmsg *bm; if (bset->exec_pid > 0) { if (debug) printf("beacon_now - still an exec under way.\n"); // Wait 3 seconds before retrying. bset->beacon_nexttime.tv_sec += 3; return; } if (bset->beacon_msgs_cursor >= bset->beacon_msgs_count) // Last done.. bset->beacon_msgs_cursor = 0; if (bset->beacon_msgs_cursor == 0) { beacon_resettimer(bset); } /* --- now the business of sending ... */ //if (debug) printf("beacon_now idx=%d\n", bset->beacon_msgs_cursor ); bm = bset->beacon_msgs[bset->beacon_msgs_cursor++]; bset->beacon_nexttime.tv_sec = bm->nexttime; bset->beacon_nexttime.tv_usec = 0; beacon_it(bset, bm); } static void beacon_it(struct beaconset *bset, struct beaconmsg *bm) { int destlen; int txtlen, msglen; int i; char const *txt; char *msg; if (debug) printf("BEACON: idx=%d, nexttime= +%d sec\n", bset->beacon_msgs_cursor-1, (int)(bset->beacon_nexttime.tv_sec - tick.tv_sec)); destlen = strlen(bm->dest) + ((bm->via != NULL) ? strlen(bm->via): 0) +2; if (bm->filename != NULL) { msg = alloca(256); // This is a load-and-discard allocation txt = msg+2; msg[0] = 0x03; msg[1] = 0xF0; if (!msg_read_file(bm->filename, msg+2, 256-2)) { // Failed loading if (debug) printf("BEACON ERROR: Failed to load anything from file %s\n",bm->filename); syslog(LOG_ERR, "Failed to load anything from beacon file %s", bm->filename); return; } } else if (bm->msg != NULL) { msg = (char*)bm->msg; txt = bm->msg+2; // Skip Control+PID bytes } else if (bm->execfile != NULL) { bset->exec_buf = realloc(bset->exec_buf, 256); bset->exec_buf[0] = 0x03; bset->exec_buf[1] = 0xF0; bset->exec_buf_length = 2; bset->exec_buf_space = 256; bset->exec_bm = bm; if (!msg_exec_file(bm->execfile, bm->timeout, bset)) { if (debug) printf("BEACON ERROR: Failed to exec file %s\n",bm->execfile); syslog(LOG_ERR, "Failed to exec file %s", bm->execfile); return; } return; // spawning done, successfull or not.. } else { if (debug) printf("Nothing to beacon now.\n"); return; } txtlen = strlen(txt); msglen = txtlen+2; // this includes the control+pid bytes /* _NO_ ending CRLF, the APRSIS subsystem adds it. */ /* Send those (rf)beacons.. (a noop if interface == NULL) */ if (bm->interface != NULL) { const char *callsign = bm->interface->callsign; const char *src = (bm->src != NULL) ? bm->src : callsign; int len = destlen + 12 + strlen(src); // destlen contains bm->via plus room for ",TCPIP*" char *destbuf = alloca(len); // Now it is time to beacon something, lets make sure // the source callsign is not APRSIS ! if (strcmp(src,"APRSIS") == 0) { if (debug) printf("CONFIGURATION ERROR: Beacon with source callsign APRSIS. Skipped!\n"); return; } if (bm->timefix) fix_beacon_time(msg, msglen); #ifndef DISABLE_IGATE if (bm->beaconmode <= 0) { if (bm->via != NULL) sprintf(destbuf,"%s>%s,%s,TCPIP*", src, bm->dest, bm->via); else sprintf(destbuf,"%s>%s,TCPIP*", src, bm->dest); if (debug) { printf("%ld\tNow beaconing to APRSIS %s '%s' -> '%s',", tick.tv_sec, callsign, destbuf, txt); printf(" next beacon in %.2f minutes\n", ((bset->beacon_nexttime.tv_sec - tick.tv_sec)/60.0)); } // Send them all also as netbeacons.. aprsis_queue(destbuf, strlen(destbuf), qTYPE_LOCALGEN, aprsis_login, txt, txtlen); } #endif if (bm->beaconmode >= 0 && bm->interface->tx_ok) { // And to interfaces char *dp = destbuf; // destbuf collects ONLY the VIA data if (strcmp(src, callsign) != 0) { if (bm->via != NULL) dp += sprintf( dp, "%s*,%s", callsign, bm->via ); else dp += sprintf( dp, "%s*", callsign ); } else { if (bm->via != NULL) dp += sprintf( dp, "%s", bm->via ); else *dp = 0; } if (debug) { printf("%ld\tNow beaconing to interface[1] %s(%s) '%s' -> '%s',", tick.tv_sec, callsign, src, destbuf, txt); printf(" next beacon in %.2f minutes\n", ((bset->beacon_nexttime.tv_sec - tick.tv_sec)/60.0)); } interface_transmit_beacon(bm->interface, src, bm->dest, destbuf, // via data msg, msglen); } } else { for ( i = 0; i < all_interfaces_count; ++i ) { const struct aprx_interface *aif = all_interfaces[i]; const char *callsign = aif->callsign; const char *src = (bm->src != NULL) ? bm->src : callsign; int len = destlen + 12 + (src != NULL ? strlen(src) : 0); // destlen contains bm->via, plus room for ",TCPIP*" char *destbuf = alloca(len); if (debug>1) printf("Beacon: aif=%p callsign='%s' src='%s' bm->dest='%s' bm->via='%s'\n", aif, callsign, src, bm->dest, bm->via); if (!interface_is_beaconable(aif)) { if (debug>1) printf("Not a beaconable interface, skipping\n"); continue; // it is not a beaconable interface } if (callsign == NULL) { // Probably KISS master interface, and subIF 0 has no definition. if (debug>1) printf("No callsign on interface interface, skipping\n"); continue; } if (aif->iftype == IFTYPE_APRSIS) { // If we have no radio interfaces, we may still // want to do beacons to APRSIS. Ignore the // builtin APRSIS interface if there are more // interfaces available! if (all_interfaces_count > 1) { if (debug>2) printf("Beaconing to APRSIS interface ignored in presence of other interfaces. Skipping.\n"); continue; // Ignore the builtin APRSIS interface } } // Now it is time to beacon something, lets make sure // the source callsign is not APRSIS ! if (strcmp(src,"APRSIS") == 0) { if (debug) printf("CONFIGURATION ERROR: Beaconing with source callsign APRSIS! Skipping.\n"); continue; } if (bm->timefix) fix_beacon_time((char*)msg, msglen); #ifndef DISABLE_IGATE if (bm->beaconmode <= 0) { // Send them all also as netbeacons.. if (bm->via != NULL) sprintf(destbuf,"%s>%s,%s,TCPIP*", src, bm->dest, bm->via); else sprintf(destbuf,"%s>%s,TCPIP*", src, bm->dest); if (debug) { printf("%ld\tNow beaconing to APRSIS %s(%s) '%s' -> '%s',", tick.tv_sec, callsign, src, destbuf, txt); printf(" next beacon in %.2f minutes\n", ((bset->beacon_nexttime.tv_sec - tick.tv_sec)/60.0)); } aprsis_queue(destbuf, strlen(destbuf), qTYPE_LOCALGEN, aprsis_login, txt, txtlen); } #endif if (bm->beaconmode >= 0 && aif->tx_ok) { // And to transmit-capable interfaces char *dp = destbuf; // destbuf collects ONLY the VIA data // The 'destbuf' has a plenty of room if (strcmp(src, callsign) != 0) { if (bm->via != NULL) dp += sprintf( dp, "%s*,%s", callsign, bm->via ); else dp += sprintf( dp, "%s*", callsign ); } else { if (bm->via != NULL) dp += sprintf( dp, "%s", bm->via ); else *dp = 0; } if (debug) { printf("%ld\tNow beaconing to interface[2] %s(%s) '%s' -> '%s',", tick.tv_sec, callsign, src, destbuf, txt); printf(" next beacon in %.2f minutes\n", ((bset->beacon_nexttime.tv_sec - tick.tv_sec)/60.0)); } interface_transmit_beacon(aif, src, bm->dest, destbuf, // via data msg, msglen); } } } } int beacon_prepoll(struct aprxpolls *app) { int i; #ifndef DISABLE_IGATE if (!aprsis_login) return 0; /* No mycall ! hoh... */ #endif for (i = 0; i < bsets_count; ++i) { struct beaconset *bset = bsets[i]; if (bset->beacon_msgs == NULL) continue; // nothing here if (time_reset) { // master time pickup noticed time back-tracking beacon_resettimer(bset); } if (tv_timercmp(&bset->beacon_nexttime, &app->next_timeout) < 0) app->next_timeout = bset->beacon_nexttime; if (bset->exec_pid != 0 && bset->exec_fd >= 0) { struct pollfd *pfd; // FD is open, lets mark it for poll read.. pfd = aprxpolls_new(app); pfd->fd = bset->exec_fd; pfd->events = POLLIN | POLLPRI; pfd->revents = 0; } } return 0; /* No poll descriptors, only time.. */ } int beacon_postpoll(struct aprxpolls *app) { int idx, i; //struct serialport *S; struct pollfd *P; #ifndef DISABLE_IGATE if (!aprsis_login) return 0; /* No mycall ! hoh... */ #endif for (i = 0; i < bsets_count; ++i) { struct beaconset *bset = bsets[i]; if (bset->exec_pid > 0 && bset->exec_deadline < tick.tv_sec) { // Waited too long, discard it. //printf("killing subprogram pid=%d mypid=%d\n", bset->exec_pid, getpid()); if (debug) printf("Killing overdue beacon exec subprogram pid %d\n", bset->exec_pid); kill(bset->exec_pid, SIGKILL); bset->exec_pid = - bset->exec_pid; } for (idx = 0, P = app->polls; idx < app->pollcount; ++idx, ++P) { if (bset->exec_fd == P->fd) { if (debug) printf("revents of exec_fd = 0x%x\n", P->revents); if (P->revents & (POLLIN | POLLPRI | POLLHUP)) { msg_exec_read(bset); } } } if (bset->beacon_msgs == NULL) continue; // nothing.. if (tv_timercmp(&bset->beacon_nexttime, &tick) > 0) continue; // not yet beacon_now(bset); } if (debug) printf("beacon_postpoll()\n"); return 0; } void beacon_childexit(int pid) { int i; for (i = 0; i < bsets_count; ++i) { struct beaconset *bset = bsets[i]; if (pid == bset->exec_pid) { bset->exec_pid = -pid; if (debug) { // Avoid stdio FILE* interlocks within signal handler char buf[64]; sprintf(buf, "matched child exit, pid=%d\n", pid); write(1, buf, strlen(buf)); } break; } } } aprx-2.08.svn593/Makefile.in0000644000175000017500000001521212313325035014520 0ustar colincolin# # APRX -- 2nd generation receive-only APRS-i-gate with # minimal requirement of esoteric facilities or # libraries of any kind beyond UNIX system libc. # # Note: This makefile uses features from GNU make # -------------------------------------------------------------------- # # target paths VARRUN= /var/run # directory for aprx.state and pid-file VARLOG= /var/log/aprx # directory for direct logfiles CFGFILE= @sysconfdir@/aprx.conf # default configuration file SBINDIR= @sbindir@ # installation path for programs MANDIR= @mandir@ # installation path for manual pages # -------------------------------------------------------------------- # srcdir = @srcdir@ VPATH = @srcdir@ @SET_MAKE@ PROF= # used by 'make profile' # Compiler and flags CC= @CC@ CFLAGS= @CFLAGS@ @CCPTHREAD@ # Linker and flags LD= @CC@ LDFLAGS= @LDFLAGS@ $(PROF) datarootdir= @datarootdir@ INSTALL= $(srcdir)/install-sh INSTALL_PROGRAM=$(INSTALL) -m 755 INSTALL_DATA= $(INSTALL) -m 644 # -------------------------------------------------------------------- # # no user serviceable parts below # -------------------------------------------------------------------- # # strip extra whitespace from paths VARRUN:=$(strip $(VARRUN)) VARLOG:=$(strip $(VARLOG)) CFGFILE:=$(strip $(CFGFILE)) SBINDIR:=$(strip $(SBINDIR)) MANDIR:=$(strip $(MANDIR)) # generate version strings VERSION = @VERSION_STRING@ SVNVERSION = $(shell cat SVNVERSION) versionupdate := $(shell if [ "$(PKG_REV)-$(PKG_RELEASE)" != "-" ]; then echo "$(PKG_REV)-$(PKG_RELEASE)" > SVNVERSION; fi) # VERSION:=$(shell cat VERSION) # SVNVERSION_CMD:=$(shell which svnversion) # SVNVERSION:=$(shell if ${SVNVERSION_CMD} > /dev/null 2>&1 \&\& test -x ${SVNVERSION_CMD} -a \( -d .svn -o -d ../.svn -o -d ../../.svn \) ; then ${SVNVERSION_CMD} | tee SVNVERSION ; else cat SVNVERSION; fi) DATE:=$(shell date +"%Y %B %d") RFCDATE:=$(shell date +"%a, %d %b %Y %H:%M:%S %z") DEFS= -DAPRXVERSION="\"@VERSION_STRING@r$(SVNVERSION)\"" \ -DVARRUN="\"$(VARRUN)\"" -DVARLOG="\"$(VARLOG)\"" \ -DCFGFILE="\"$(CFGFILE)\"" # program names PROGAPRX= aprx PROGSTAT= $(PROGAPRX)-stat LIBS= @LIBS@ @LIBRESOLV@ @LIBSOCKET@ @LIBM@ @LIBPTHREAD@ @LIBGETADDRINFO@ @LIBRT@ OBJSAPRX= aprx.o ttyreader.o ax25.o aprsis.o beacon.o config.o \ netax25.o erlang.o aprxpolls.o telemetry.o igate.o \ cellmalloc.o historydb.o keyhash.o parse_aprs.o \ dupecheck.o kiss.o interface.o pbuf.o digipeater.o \ valgrind.o filter.o dprsgw.o crc.o agwpesocket.o \ netresolver.o timercmp.o #ssl.o OBJSSTAT= erlang.o aprx-stat.o aprxpolls.o valgrind.o timercmp.o # man page sources, will be installed as $(PROGAPRX).8 / $(PROGSTAT).8 MANAPRX := aprx.8 MANSTAT := aprx-stat.8 OBJS= $(OBJSAPRX) $(OBJSSTAT) MAN= $(MANAPRX) $(MANSTAT) # -------------------------------------------------------------------- # .PHONY: all all: $(PROGAPRX) $(PROGSTAT) man aprx.conf aprx-complex.conf valgrind: @echo "Did you do 'make clean' before 'make valgrind' ?" make all CFLAGS="${CFLAGS} -D_FOR_VALGRIND_" profile: @echo "Did you do 'make clean' before 'make profile' ?" make all PROF="-pg" $(PROGAPRX): $(OBJSAPRX) VERSION Makefile $(LD) $(LDFLAGS) -o $@ $(OBJSAPRX) $(LIBS) $(PROGSTAT): $(OBJSSTAT) VERSION Makefile $(LD) $(LDFLAGS) -o $@ $(OBJSSTAT) $(LIBS) .PHONY: man man: $(MAN) .PHONY: doc html pdf doc: html pdf pdf: $(MAN:=.pdf) html: $(MAN:=.html) # -------------------------------------------------------------------- # .PHONY: install install-deb install: all $(INSTALL_PROGRAM) $(PROGAPRX) $(DESTDIR)$(SBINDIR)/$(PROGAPRX) $(INSTALL_PROGRAM) $(PROGSTAT) $(DESTDIR)$(SBINDIR)/$(PROGSTAT) $(INSTALL_DATA) $(MANAPRX) $(DESTDIR)$(MANDIR)/man8/$(PROGAPRX).8 $(INSTALL_DATA) $(MANSTAT) $(DESTDIR)$(MANDIR)/man8/$(PROGSTAT).8 if [ ! -f $(DESTDIR)$(CFGFILE) ] ; then \ $(INSTALL_DATA) aprx.conf $(DESTDIR)$(CFGFILE) ; \ else true ; fi .PHONY: clean clean: rm -f $(PROGAPRX) $(PROGSTAT) rm -f $(MAN) $(MAN:=.html) $(MAN:=.ps) $(MAN:=.pdf) \ rm -f aprx.conf logrotate.aprx rm -f *~ *.o *.d .PHONY: distclean distclean: clean rm -f config.log config.status config.h rm -rf autom4te.cache *.log* doc/.~*# # -------------------------------------------------------------------- # %.o: %.c VERSION Makefile $(CC) $(CFLAGS) $(PROF) $(DEFS) -c $< @$(CC) -MM $(CFLAGS) $(PROF) $(DEFS) $< > $(@:.o=.d) $(MAN:=.html): %.html : % sh man-to-html.sh $< > $@ $(MAN:=.ps): %.ps : % groff -man $< > $@ $(MAN:=.pdf): %.pdf : %.ps ps2pdf $< logrotate.aprx $(MAN) aprx-complex.conf aprx.conf: % : %.in VERSION Makefile perl -ne "s{\@DATEVERSION\@}{$(VERSION) - $(DATE)}g; \ s{\@VARRUN\@}{$(VARRUN)}g; \ s{\@VARLOG\@}{$(VARLOG)}g; \ s{\@CFGFILE\@}{$(CFGFILE)}g; \ print;" \ < $< > $@ # -------------------------------------------------------------------- # # # Following is for the original author only... # DISTVERSION:=aprx-$(VERSION).svn$(SVNVERSION) DISTTARGET:=../$(DISTVERSION) RPMVERSION:=$(shell echo "${DISTVERSION}" | sed -e 's/aprx-//') .PHONY: dist svnversion-test svnversion-test: # Special for the source maintainer only.. @sh svnversion-test.sh $(SVNVERSION) dist: svnversion-test if [ ! -d $(DISTTARGET) ] ; then \ mkdir $(DISTTARGET) ; \ fi tar cf - --exclude-backups --exclude-vcs --exclude=windows --exclude=*.log* --exclude=*.conf . | (cd $(DISTTARGET) ; tar xf -) echo "$(DISTVERSION)" > $(DISTTARGET)/VERSION perl -ne "\$$ver = '$(DISTVERSION)'; \ \$$ver =~ tr/0-9.//cd; \ \$$ver .= '-1'; \ s{\@VERSION\@}{\$$ver}g; \ s{\@RFCDATE\@}{$(RFCDATE)}g; \ print;" \ < $(DISTTARGET)/debian/changelog.release \ > $(DISTTARGET)/debian/changelog rm -f $(DISTTARGET)/debian/changelog.release rm -f $(DISTTARGET)/aprx.spec perl -ne "s{\@VERSION\@}{$(RPMVERSION)}g; \ s{\@DATE0\@}{$(DATE0)}g; \ print;" \ < $(DISTTARGET)/rpm/aprx.spec.in \ > $(DISTTARGET)/aprx.spec rm -f $(DISTTARGET)/rpm/aprx.spec.in make -C $(DISTTARGET) distclean cd .. && \ tar czvf $(DISTVERSION).tar.gz $(DISTVERSION) # -------------------------------------------------------------------- # .PHONY: make-deb make-rpm make-deb: if [ -f debian/changelog.release ] ; then \ perl -ne "\$$ver = '$(DISTVERSION)'; \ \$$ver =~ tr/0-9.//cd; \ \$$ver .= '-1'; \ s{\@VERSION\@}{\$$ver}g; \ s{\@RFCDATE\@}{$(RFCDATE)}g; \ print;" \ < debian/changelog.release \ > debian/changelog ; \ fi dpkg-buildpackage -b -us -uc -rfakeroot make-rpm: # actually just a reminder of how to do it.. rpmbuild --target i386 -ta ../$(DISTVERSION).tar.gz # -------------------------------------------------------------------- # # include object depencies if available -include $(OBJS:.o=.d) aprx-2.08.svn593/pbuf.c0000644000175000017500000001603112314016324013552 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #define _SVID_SOURCE 1 #include "aprx.h" /* * - Allocate pbuf * - Free pbuf * - Handle refcount (get/put) */ #ifndef _FOR_VALGRIND_ static cellarena_t *pbuf_cells; #endif // int pbuf_size = sizeof(struct pbuf_t); // 152 bytes on i386 // int pbuf_alignment = __alignof__(struct pbuf_t); // 8 on i386 // 2150 byte pbuf takes in an AX.25 packet of about 1kB in size, // and in APRS use there never should be larger than about 512 bytes. // A 16 kB arena fits in 7 of these humongous pbufs. const int pbufcell_size = sizeof(struct pbuf_t) + 2150; const int pbufcell_align = __alignof__(struct pbuf_t); void pbuf_init(void) { #ifndef _FOR_VALGRIND_ /* A _few_... */ pbuf_cells = cellinit( "filter", pbufcell_size, pbufcell_align, CELLMALLOC_POLICY_LIFO, 16, // 16 kB at the time 0 // minfree ); #endif } static void pbuf_free(struct pbuf_t *pb) { #ifndef _FOR_VALGRIND_ cellfree(pbuf_cells, pb); #else free(pb); #endif if (debug > 1) printf("pbuf_free(%p)\n",pb); } static struct pbuf_t *pbuf_alloc( const int axlen, const int tnc2len ) { int pblen = sizeof(struct pbuf_t) + axlen + tnc2len + 2; #ifndef _FOR_VALGRIND_ // Picks suitably sized pbuf, and pre-cleans it // before passing to user struct pbuf_t *pb; if (pblen > 2150) { // Outch! return NULL; } pb = cellmalloc(pbuf_cells); memset(pb, 0, pblen ); #else // No size limits with valgrind.. struct pbuf_t *pb = calloc( 1, pblen ); #endif if (debug > 1) printf("pbuf_alloc(%d,%d) -> %p\n",axlen,tnc2len,pb); pb->packet_len = tnc2len; pb->buf_len = tnc2len; pb->data[tnc2len] = 0; pb->ax25addr = (uint8_t*)pb->data + tnc2len + 1; return pb; } struct pbuf_t *pbuf_get( struct pbuf_t *pb ) { // Increments refcount pb->refcount += 1; return pb; } void pbuf_put( struct pbuf_t *pb ) { // Decrements refcount, if 0 -> free()! pb->refcount -= 1; if (pb->refcount == 0) pbuf_free(pb); } static struct pbuf_t *_pbuf_new(const int is_aprs, const int digi_like_aprs, const int axlen, const int tnc2len); static struct pbuf_t *_pbuf_new(const int is_aprs, const int digi_like_aprs, const int axlen, const int tnc2len) { struct pbuf_t *pb = pbuf_alloc( axlen, tnc2len ); if (pb == NULL) return NULL; pbuf_get(pb); pb->is_aprs = is_aprs; pb->digi_like_aprs = digi_like_aprs; pb->t = tick.tv_sec; // Arrival time return pb; } // Do the pbuf filling in single location, processes the TNC2 header data struct pbuf_t * pbuf_new( const int is_aprs, const int digi_like_aprs, const int tnc2addrlen, const char *tnc2buf, const int tnc2len, const int ax25addrlen, const void *ax25buf, const int ax25len ) { char *p; char *src_end; /* pointer to the > after srccall */ char *path_start; /* pointer to the start of the path */ const char *path_end; /* pointer to the : after the path */ const char *packet_end; /* pointer to the end of the packet */ const char *info_start; /* pointer to the beginning of the info */ const char *info_end; /* end of the info */ char *dstcall_end_or_ssid; /* end of dstcall, before SSID ([-:,]) */ char *dstcall_end; /* end of dstcall including SSID ([:,]) */ char *via_start; /* start of the digipeater path (after dstcall,) */ // const char *data; /* points to original incoming path/payload separating ':' character */ // int datalen; /* length of the data block excluding tail \r\n */ int pathlen; /* length of the path == data-s */ struct pbuf_t *pb; /* a packet looks like: * SRCCALL>DSTCALL,PATH,PATH:INFO\r\n * (we have normalized the \r\n by now) * * The tnc2addrlen is index of the first ':'. */ path_end = tnc2buf + tnc2addrlen; pathlen = tnc2addrlen; // data = path_end; // Begins with ":" // datalen = tnc2len - pathlen; // Not including line end \r\n packet_end = tnc2buf + tnc2len; // Just to compare against far end.. /* look for the '>' */ src_end = memchr(tnc2buf, '>', pathlen < CALLSIGNLEN_MAX+1 ? pathlen : CALLSIGNLEN_MAX+1); if (!src_end) { return NULL; // No ">" in packet start.. } path_start = src_end+1; if (path_start >= packet_end) { // We're already at the path end return NULL; } if (src_end - tnc2buf > CALLSIGNLEN_MAX || src_end - tnc2buf < CALLSIGNLEN_MIN) { return NULL; /* too long source callsign */ } info_start = path_end+1; // @":"+1 - first char of the payload if (info_start >= packet_end) { return NULL; } /* see that there is at least some data in the packet */ info_end = packet_end; if (info_end <= info_start) { return NULL; } /* look up end of dstcall (excluding SSID - this is the way dupecheck and * mic-e parser wants it) */ dstcall_end = path_start; while (dstcall_end < path_end && *dstcall_end != '-' && *dstcall_end != ',' && *dstcall_end != ':') dstcall_end++; dstcall_end_or_ssid = dstcall_end; // OK, SSID is here (or the dstcall end), go for the real end while (dstcall_end < path_end && *dstcall_end != ',' && *dstcall_end != ':') dstcall_end++; if (dstcall_end - path_start > CALLSIGNLEN_MAX) { return NULL; /* too long for destination callsign */ } /* where does the digipeater path start? */ via_start = dstcall_end; while (via_start < path_end && (*via_start != ',' && *via_start != ':')) { via_start++; } pb = _pbuf_new( is_aprs, digi_like_aprs, ax25len, tnc2len ); if (!pb) { // This should never happen... return NULL; } // copy TNC2 data to its area p = pb->data; memcpy(p, tnc2buf, tnc2len); p += tnc2len; // Copy AX.25 data to its area.. memcpy(pb->ax25addr, ax25buf, ax25len); pb->ax25addrlen = ax25addrlen; pb->ax25data = pb->ax25addr + ax25addrlen; pb->ax25datalen = ax25len - ax25addrlen; // How much there really is data? pb->packet_len = tnc2len; packet_end = p; /* for easier overflow checking expressions */ /* fill necessary info for parsing and dupe checking in the packet buffer */ pb->srcname = pb->data; pb->srcname_len = src_end - tnc2buf; pb->srccall_end = pb->data + (src_end - tnc2buf); // "srccall>.." <-- @'>' pb->dstcall_end_or_ssid = pb->data + (dstcall_end_or_ssid - tnc2buf); pb->dstcall_end = pb->data + (dstcall_end - tnc2buf); pb->dstcall_len = via_start - src_end - 1; pb->info_start = pb->data + tnc2addrlen + 1; return pb; } aprx-2.08.svn593/erlang.c0000644000175000017500000004271312305424764014107 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" #include #include #include #include /* The erlang module accounts data reception per 1m/10m/60m intervals, and reports them on verbout.. */ /* #define USE_ONE_MINUTE_INTERVAL 1 */ static struct timeval erlang_time_end_1min; static float erlang_time_ival_1min = 1.0; static struct timeval erlang_time_end_10min; static float erlang_time_ival_10min = 1.0; #ifdef ERLANGSTORAGE static struct timeval erlang_time_end_60min; static float erlang_time_ival_60min = 1.0; #endif #ifdef ERLANGSTORAGE static const char *erlangtitle = "APRX SNMP + Erlang dataset\n"; #endif int erlangsyslog; /* if set, will log via syslog(3) */ int erlanglog1min; /* if set, will log also "ERLANG1" interval */ const char *erlanglogfile; const char *erlang_backingstore = VARRUN "/aprx.state"; #ifdef ERLANGSTORAGE static int erlang_file_fd = -1; static int erlang_mmap_size; #endif static void *erlang_mmap; struct erlanghead *ErlangHead; struct erlangline **ErlangLines; int ErlangLinesCount; int erlang_data_is_nonshared; /* In embedded target.. */ struct erlang_file { struct erlanghead head; struct erlangline lines[1]; }; static void erlang_backingstore_startops(void) { ErlangHead->server_pid = getpid(); ErlangHead->start_time = time(NULL); if (!mycall) strncpy(ErlangHead->mycall, "N0CALL", sizeof(ErlangHead->mycall)); else strncpy(ErlangHead->mycall, mycall, sizeof(ErlangHead->mycall)); ErlangHead->mycall[sizeof(ErlangHead->mycall) - 1] = 0; /* NUL terminate */ } static int erlang_backingstore_grow(int do_create, int add_count) { struct erlang_file *EF; int i; #ifdef ERLANGSTORAGE struct stat st; char buf[256]; int new_size, pagesize = sysconf(_SC_PAGE_SIZE); int doing_init = 0; if (erlang_data_is_nonshared) goto embedded_only; if (erlang_file_fd < 0) { goto embedded_only; } fstat(erlang_file_fd, &st); lseek(erlang_file_fd, 0, SEEK_END); new_size = st.st_size; if (new_size % pagesize) { new_size /= pagesize; ++new_size; new_size *= pagesize; } if (new_size == 0) { new_size = pagesize; doing_init = 1; } /* new_size expanded to be exact page size multiple. */ /* If the new size is larger than the file size.. .. and at least one page size (e.g. 4 kB) .. */ if (new_size > st.st_size) { /* .. then we fill in the file to given size. */ int i, rc, l; i = st.st_size; memset(buf, 0, sizeof(buf)); lseek(erlang_file_fd, 0, SEEK_END); while (i < new_size) { l = sizeof(buf); if (new_size - i < l) l = new_size - i; rc = write(erlang_file_fd, buf, l); if (rc < 0 && errno == EINTR) continue; if (rc != l) break; i += rc; } } redo_open:; if (erlang_mmap) { msync(erlang_mmap, erlang_mmap_size, MS_SYNC); munmap(erlang_mmap, erlang_mmap_size); erlang_mmap = NULL; erlang_mmap_size = 0; ErlangHead = NULL; } /* Some (early Linux) systems mmap() offset on IO pointer... */ lseek(erlang_file_fd, 0, SEEK_SET); fstat(erlang_file_fd, &st); erlang_mmap_size = st.st_size; erlang_mmap = mmap(NULL, erlang_mmap_size, PROT_READ | (do_create ? PROT_WRITE : 0), MAP_SHARED, erlang_file_fd, 0); if (erlang_mmap == MAP_FAILED) { erlang_mmap = NULL; syslog(LOG_ERR, "Erlang-file mmap() failed, fd=%d, errno=%d: %s", erlang_file_fd, errno, strerror(errno)); } if (erlang_mmap) { int rc, l; EF = erlang_mmap; ErlangHead = &EF->head; if (EF->head.version != ERLANGLINE_STRUCT_VERSION || EF->head.last_update == 0) { if (doing_init) { /* Not initialized ? */ memset(erlang_mmap, 0, erlang_mmap_size); strcpy(EF->head.title, erlangtitle); EF->head.version = ERLANGLINE_STRUCT_VERSION; EF->head.linecount = 0; EF->head.last_update = tick.tv_sec; ErlangLinesCount = 0; } else { /* Wrong head magic, and not doing block init.. */ munmap(erlang_mmap, erlang_mmap_size); erlang_mmap = NULL; erlang_mmap_size = 0; syslog(LOG_ERR, "Erlang-file has bad magic in it, not opening! Not modifying!"); close(erlang_file_fd); erlang_file_fd = -1; goto embedded_only; /* BAD BAD ! */ } } if (EF->head.linecount != ErlangLinesCount || add_count > 0) { /* must resize.. */ int new_count = EF->head.linecount + add_count; new_size = sizeof(struct erlang_file) + sizeof(struct erlangline) * (new_count - 1); if (new_size % pagesize) { new_size /= pagesize; ++new_size; new_size *= pagesize; } i = st.st_size; memset(buf, 0, sizeof(buf)); lseek(erlang_file_fd, 0, SEEK_END); /* append on the file.. */ while (i < new_size) { l = sizeof(buf); if (new_size - i < l) l = new_size - i; rc = write(erlang_file_fd, buf, l); if (rc < 0 && errno == EINTR) continue; if (rc != l) break; i += rc; } if (i < new_size) { munmap(erlang_mmap, erlang_mmap_size); erlang_mmap = NULL; goto embedded_only; /* BAD BAD ! */ } add_count = 0; if (do_create) EF->head.linecount = new_count; ErlangLinesCount = new_count; goto redo_open; /* redo mapping */ } /* Ok, successfull open, correct linecount */ ErlangLines = (void *) realloc((void *) ErlangLines, (ErlangLinesCount + 1) * sizeof(void *)); for (i = 0; i < ErlangLinesCount; ++i) { ErlangLines[i] = &EF->lines[i]; } return 0; /* OK ! */ } embedded_only:; #endif /* ... ERLANGSTORAGE ... */ erlang_data_is_nonshared = 1; if (add_count > 0 || !erlang_mmap) { ErlangLinesCount += add_count; erlang_mmap = realloc(erlang_mmap, sizeof(*EF) + (ErlangLinesCount + 1) * sizeof(struct erlangline)); } EF = erlang_mmap; ErlangHead = &EF->head; /* Ok, successfull open, correct linecount */ ErlangLines = (void *) realloc((void *) ErlangLines, (ErlangLinesCount + 1) * sizeof(void *)); for (i = 0; i < ErlangLinesCount; ++i) { ErlangLines[i] = &EF->lines[i]; } return 0; } static int erlang_backingstore_open(int do_create) { #ifdef ERLANGSTORAGE if (!erlang_backingstore) { syslog(LOG_ERR, "erlang_backingstore not defined!"); erlang_data_is_nonshared = 1; } if (erlang_file_fd < 0 && erlang_backingstore) { erlang_file_fd = open(erlang_backingstore, do_create ? O_RDWR : O_RDONLY, 0644); /* Presume: it exists! */ if ((erlang_file_fd < 0) && do_create && (errno == ENOENT)) { erlang_file_fd = open(erlang_backingstore, O_RDWR | O_CREAT | O_EXCL, 0644); } } if (erlang_file_fd < 0) { syslog(LOG_ERR, "Open of '%s' for erlang_backingstore file failed! errno=%d: %s", erlang_backingstore, errno, strerror(errno)); erlang_data_is_nonshared = 1; } #endif return erlang_backingstore_grow(do_create, 0); /* Just open */ } static struct erlangline *erlang_findline(const char *portname, int bytes_per_minute) { int i; struct erlangline *E; if (portname == NULL) return NULL; if (bytes_per_minute == 0) bytes_per_minute = (int) ((1200.0 * 60) / 8.2); // Default of 1200 bps /* Allocate a new ErlangLines[] entry for this object, if no existing one is found.. */ E = NULL; if (ErlangLines) { for (i = 0; i < ErlangLinesCount; ++i) { if (strcmp(portname, ErlangLines[i]->name) == 0) { /* HOO-RAY! It is this one! */ E = ErlangLines[i]; break; } } } /* If found -- err... why we are SETing it AGAIN ? */ if (!E) { /* Allocate a new one */ erlang_backingstore_grow(1, 1); if (!ErlangLines) return NULL; /* D'uh! */ E = ErlangLines[ErlangLinesCount - 1]; /* Last one is the lattest.. */ memset(E, 0, sizeof(*E)); strncpy(E->name, portname, sizeof(E->name) - 1); E->name[sizeof(E->name) - 1] = 0; E->erlang_capa = bytes_per_minute; E->index = ErlangLinesCount - 1; #ifdef ERLANGSTORAGE E->e1_cursor = 0; E->e1_max = APRXERL_1M_COUNT; E->e10_cursor = 0; E->e10_max = APRXERL_10M_COUNT; E->e60_cursor = 0; E->e60_max = APRXERL_60M_COUNT; #else #if (USE_ONE_MINUTE_DATA == 1) E->e1_cursor = 0; E->e1_max = APRXERL_1M_COUNT; #else E->e10_cursor = 0; E->e10_max = APRXERL_10M_COUNT; #endif #endif } return E; } static void erlang_timer_init(void *dummy) { /* Time intervals will end at next even 1 minute/10 minutes/60 minutes, although said interval will be shorter than full. */ erlang_time_end_1min.tv_sec = tick.tv_sec + 60 - (tick.tv_sec % 60); erlang_time_end_1min.tv_usec = 0; erlang_time_ival_1min = (float) (60 - tick.tv_sec % 60) / 60.0; erlang_time_end_10min.tv_sec = tick.tv_sec + 600 - (tick.tv_sec % 600); erlang_time_end_10min.tv_usec = 0; erlang_time_ival_10min = (float) (600 - tick.tv_sec % 600) / 600.0; #ifdef ERLANGSTORAGE erlang_time_end_60min.tv_sec = tick.tv_sec + 3600 - (tick.tv_sec % 3600); erlang_time_end_60min.tv_usec = 0; erlang_time_ival_60min = (float) (3600 - tick.tv_sec % 3600) / 3600.0; #endif } /* * erlang_set() */ void erlang_set(const char *portname, int bytes_per_minute) { erlang_findline(portname, bytes_per_minute); } /* * erlang_add() */ void erlang_add(const char *portname, ErlangMode erl, int bytes, int packets) { struct erlangline *E; if (!portname) return; E = erlang_findline(portname, (int) ((1200.0 * 60) / 8.2)); if (debug > 1) printf("erlang_add(%s, %s, %d, %d)\n", portname, (erl == ERLANG_RX ? "RX":(erl == ERLANG_TX ? "TX": "DROP")), bytes, packets); if (!E) return; if (erl == ERLANG_RX) { E->SNMP.bytes_rx += bytes; E->SNMP.packets_rx += packets; E->SNMP.update = tick.tv_sec; E->last_update = tick.tv_sec; #ifdef ERLANGSTORAGE E->erl1m.bytes_rx += bytes; E->erl1m.packets_rx += packets; E->erl1m.update = tick.tv_sec; E->erl10m.bytes_rx += bytes; E->erl10m.packets_rx += packets; E->erl10m.update = tick.tv_sec; E->erl60m.bytes_rx += bytes; E->erl60m.packets_rx += packets; E->erl60m.update = tick.tv_sec; #else #if (USE_ONE_MINUTE_STORAGE == 1) E->erl1m.bytes_rx += bytes; E->erl1m.packets_rx += packets; E->erl1m.update = tick.tv_sec; #else E->erl10m.bytes_rx += bytes; E->erl10m.packets_rx += packets; E->erl10m.update = tick.tv_sec; #endif #endif } if (erl == ERLANG_TX) { E->SNMP.bytes_tx += bytes; E->SNMP.packets_tx += packets; E->SNMP.update = tick.tv_sec; E->last_update = tick.tv_sec; #ifdef ERLANGSTORAGE E->erl1m.bytes_tx += bytes; E->erl1m.packets_tx += packets; E->erl1m.update = tick.tv_sec; E->erl10m.bytes_tx += bytes; E->erl10m.packets_tx += packets; E->erl10m.update = tick.tv_sec; E->erl60m.bytes_tx += bytes; E->erl60m.packets_tx += packets; E->erl60m.update = tick.tv_sec; #else #if (USE_ONE_MINUTE_STORAGE == 1) E->erl1m.bytes_tx += bytes; E->erl1m.packets_tx += packets; E->erl1m.update = tick.tv_sec; #else E->erl10m.bytes_tx += bytes; E->erl10m.packets_tx += packets; E->erl10m.update = tick.tv_sec; #endif #endif } if (erl == ERLANG_DROP) { E->SNMP.bytes_rxdrop += bytes; E->SNMP.packets_rxdrop += packets; E->SNMP.update = tick.tv_sec; E->last_update = tick.tv_sec; #ifdef ERLANGSTORAGE E->erl1m.bytes_rxdrop += bytes; E->erl1m.packets_rxdrop += packets; E->erl1m.update = tick.tv_sec; E->erl10m.bytes_rxdrop += bytes; E->erl10m.packets_rxdrop += packets; E->erl10m.update = tick.tv_sec; E->erl60m.bytes_rxdrop += bytes; E->erl60m.packets_rxdrop += packets; E->erl60m.update = tick.tv_sec; #else #if (USE_ONE_MINUTE_STORAGE == 1) E->erl1m.bytes_rxdrop += bytes; E->erl1m.packets_rxdrop += packets; E->erl1m.update = tick.tv_sec; #else E->erl10m.bytes_rxdrop += bytes; E->erl10m.packets_rxdrop += packets; E->erl10m.update = tick.tv_sec; #endif #endif } } /* * erlang_time_end() - process erlang measurement interval time end event */ static void erlang_time_end(void) { int i; char msgbuf[500]; char logtime[40]; FILE *fp = NULL; if (erlanglogfile) { /* actually we want it to the erlanglogfile... */ fp = fopen(erlanglogfile, "a"); } printtime(logtime, sizeof(logtime)); if (tv_timercmp(&tick, &erlang_time_end_1min) >= 0) { erlang_time_end_1min.tv_sec += 60; #if (defined(ERLANGSTORAGE) || (USE_ONE_MINUTE_STORAGE == 1)) for (i = 0; i < ErlangLinesCount; ++i) { struct erlangline *E = ErlangLines[i]; E->last_update = tick.tv_sec; if (erlanglog1min) { sprintf(msgbuf, "ERLANG%-2d %s Rx %6ld %3ld Dp %6ld %3ld Tx %6ld %3ld : %5.3f %5.3f %5.3f", 1, E->name, E->erl1m.bytes_rx, E->erl1m.packets_rx, E->erl1m.bytes_rxdrop, E->erl1m.packets_rxdrop, E->erl1m.bytes_tx, E->erl1m.packets_tx, ((float) E->erl1m.bytes_rx / (float) E->erlang_capa * erlang_time_ival_1min), ((float) E->erl1m.bytes_rxdrop / (float) E->erlang_capa * erlang_time_ival_1min), ((float)E->erl1m.bytes_tx / (float)E->erlang_capa * erlang_time_ival_1min) ); if (fp) fprintf(fp, "%s %s\n", logtime, msgbuf); else if (erlangout) printf("%ld\t%s\n", tick.tv_sec, msgbuf); if (erlangsyslog) syslog(LOG_INFO, "%ld %s", tick.tv_sec, msgbuf); } E->erl1m.update = tick.tv_sec; E->e1[E->e1_cursor] = E->erl1m; ++E->e1_cursor; if (E->e1_cursor >= E->e1_max) E->e1_cursor = 0; memset(&E->erl1m, 0, sizeof(E->erl1m)); E->erl1m.update = tick.tv_sec; } erlang_time_ival_1min = 1.0; #endif } if (tv_timercmp(&tick, &erlang_time_end_10min) >= 0) { erlang_time_end_10min.tv_sec += 600; #if (defined(ERLANGSTORAGE) || (USE_ONE_MINUTE_STORAGE == 0)) for (i = 0; i < ErlangLinesCount; ++i) { struct erlangline *E = ErlangLines[i]; E->last_update = tick.tv_sec; sprintf(msgbuf, "ERLANG%-2d %s Rx %6ld %3ld Dp %6ld %3ld Tx %6ld %3ld : %5.3f %5.3f %5.3f", 10, E->name, E->erl10m.bytes_rx, E->erl10m.packets_rx, E->erl10m.bytes_rxdrop, E->erl10m.packets_rxdrop, E->erl10m.bytes_tx, E->erl10m.packets_tx, ((float) E->erl10m.bytes_rx / ((float) E->erlang_capa * 10.0 * erlang_time_ival_10min)), ((float) E->erl10m.bytes_rxdrop / ((float) E->erlang_capa * 10.0 * erlang_time_ival_10min)), ((float)E->erl10m.bytes_tx / ((float)E->erlang_capa * 10.0 * erlang_time_ival_10min)) ); if (fp) fprintf(fp, "%s %s\n", logtime, msgbuf); else if (erlangout) printf("%ld\t%s\n", tick.tv_sec, msgbuf); if (erlangsyslog) syslog(LOG_INFO, "%ld %s", tick.tv_sec, msgbuf); E->erl10m.update = tick.tv_sec; E->e10[E->e10_cursor] = E->erl10m; ++E->e10_cursor; if (E->e10_cursor >= E->e10_max) E->e10_cursor = 0; memset(&E->erl10m, 0, sizeof(E->erl10m)); E->erl10m.update = tick.tv_sec; } erlang_time_ival_10min = 1.0; #endif } #ifdef ERLANGSTORAGE if (tv_timercmp(&tick, &erlang_time_end_60min) >= 0) { erlang_time_end_60min.tv_sec += 3600; for (i = 0; i < ErlangLinesCount; ++i) { struct erlangline *E = ErlangLines[i]; /* E->last_update = now.tv_sec; -- the 10 minute step does also this */ sprintf(msgbuf, "ERLANG%-2d %s Rx %6ld %3ld Dp %6ld %3ld Tx %6ld %3ld : %5.3f %5.3f %5.3f", 60, E->name, E->erl60m.bytes_rx, E->erl60m.packets_rx, E->erl60m.bytes_rxdrop, E->erl60m.packets_rxdrop, E->erl60m.bytes_tx, E->erl60m.packets_tx, ((float) E->erl60m.bytes_rx / ((float) E->erlang_capa * 60.0 * erlang_time_ival_60min)), ((float) E->erl60m.bytes_rxdrop / ((float) E->erlang_capa * 60.0 * erlang_time_ival_60min)), ((float)E->erl60m.bytes_tx / ((float)E->erlang_capa * 60.0 * erlang_time_ival_60min)) ); if (fp) fprintf(fp, "%s %s\n", logtime, msgbuf); else if (erlangout) printf("%ld\t%s\n", tick.tv_sec, msgbuf); if (erlangsyslog) syslog(LOG_INFO, "%ld %s", tick.tv_sec, msgbuf); E->erl60m.update = tick.tv_sec; E->e60[E->e60_cursor] = E->erl60m; ++E->e60_cursor; if (E->e60_cursor >= E->e60_max) E->e60_cursor = 0; memset(&E->erl60m, 0, sizeof(E->erl60m)); E->erl60m.update = tick.tv_sec; } erlang_time_ival_60min = 1.0; } #endif if (fp) fclose(fp); } int erlang_prepoll(struct aprxpolls *app) { if (time_reset) { if (debug) printf("erlang_timer_init() to be called\n"); erlang_timer_init(NULL); } if (tv_timercmp(&app->next_timeout, &erlang_time_end_1min) > 0) app->next_timeout = erlang_time_end_1min; if (tv_timercmp(&app->next_timeout, &erlang_time_end_10min) > 0) app->next_timeout = erlang_time_end_10min; #ifdef ERLANGSTORAGE if (tv_timercmp(&app->next_timeout, &erlang_time_end_60min) > 0) app->next_timeout = erlang_time_end_60min; #endif return 0; } int erlang_postpoll(struct aprxpolls *app) { if (tv_timercmp(&tick, &erlang_time_end_1min) >= 0 || tv_timercmp(&tick, &erlang_time_end_10min) >= 0 #ifdef ERLANGSTORAGE || tv_timercmp(&tick, &erlang_time_end_60min) >= 0 #endif ) erlang_time_end(); return 0; } void erlang_init(const char *syslog_facility_name) { erlang_timer_init(NULL); } void erlang_start(int do_create) { erlang_backingstore_open(do_create); if (do_create > 1) erlang_backingstore_startops(); } aprx-2.08.svn593/cellmalloc.c0000644000175000017500000001746212314016324014736 0ustar colincolin/******************************************************************** * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * ********************************************************************/ #include "config.h" #include #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include #include #include // #if defined(HAVE_PTHREAD_CREATE) && defined(ENABLE_PTHREAD) // #include // #endif #include "cellmalloc.h" #define NO_MMAP_ON_CELLMALLOC #define MEMDEBUG /* * cellmalloc() -- manages arrays of cells of data * */ struct cellhead; struct cellarena_t { int cellsize; int alignment; int increment; /* alignment overhead applied.. */ int lifo_policy; int minfree; const char *arenaname; // pthread_mutex_t mutex; // we have a mutex-less usage environment! struct cellhead *free_head; struct cellhead *free_tail; int freecount; int createsize; #ifdef MEMDEBUG int cellblocks_count; #define CELLBLOCKS_MAX 40 /* track client cell allocator limit! */ char *cellblocks[CELLBLOCKS_MAX]; /* ref as 'char pointer' for pointer arithmetics... */ #endif }; #define CELLHEAD_DEBUG 0 struct cellhead { #if CELLHEAD_DEBUG == 1 struct cellarena_t *ca; #endif struct cellhead *next; }; /* * new_cellblock() -- must be called MUTEX PROTECTED * */ int new_cellblock(cellarena_t *ca) { int i; char *cb; #ifdef MEMDEBUG /* External backing-store files, unique ones for each cellblock, which at Linux names memory blocks in /proc/nnn/smaps "file" with this filename.. */ int fd; char name[2048]; sprintf(name, "/tmp/.-%d-%s-%d.mmap", getpid(), ca->arenaname, ca->cellblocks_count ); unlink(name); fd = open(name, O_RDWR|O_CREAT, 644); unlink(name); if (fd >= 0) { memset(name, 0, sizeof(name)); i = 0; while (i < ca->createsize) { int rc = write(fd, name, sizeof(name)); if (rc < 0) break; i += rc; } } cb = mmap( NULL, ca->createsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (fd >= 0) close(fd); #else #ifndef MAP_ANON # define MAP_ANON 0 #endif #ifdef NO_MMAP_ON_CELLMALLOC cb = malloc( ca->createsize ); #else cb = mmap( NULL, ca->createsize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); #endif #endif if (cb == NULL || cb == (char*)-1) return -1; #ifdef MEMDEBUG if (ca->cellblocks_count >= CELLBLOCKS_MAX) return -1; ca->cellblocks[ca->cellblocks_count++] = cb; #endif for (i = 0; i <= ca->createsize-ca->increment; i += ca->increment) { struct cellhead *ch = (struct cellhead *)(cb + i); /* pointer arithmentic! */ if (!ca->free_head) { ca->free_head = ch; } else { ca->free_tail->next = ch; } ca->free_tail = ch; ch->next = NULL; #if CELLHEAD_DEBUG == 1 ch->ca = ca; // cellhead pointer space #endif ca->freecount += 1; } return 0; } /* * cellinit() -- the main program calls this once for each used cell type/size * */ cellarena_t *cellinit( const char *arenaname, const int cellsize, const int alignment, const int policy, const int createkb, const int minfree ) { cellarena_t *ca = calloc(1, sizeof(*ca)); // int n; ca->arenaname = arenaname; #if CELLHEAD_DEBUG == 1 if (alignment < __alignof__(void*)) alignment = __alignof__(void*); // cellhead pointer space #endif ca->cellsize = cellsize; ca->alignment = alignment; ca->minfree = minfree; #if CELLHEAD_DEBUG == 1 ca->increment = cellsize + sizeof(void*); // cellhead pointer space #else ca->increment = cellsize; #endif if ((cellsize % alignment) != 0) { ca->increment += alignment - cellsize % alignment; } ca->lifo_policy = policy & CELLMALLOC_POLICY_LIFO; ca->createsize = createkb * 1024; #if !defined(MEMDEBUG) && defined(NO_MMAP_ON_CELLMALLOC) ca->createsize -= 16; #endif // n = ca->createsize / ca->increment; // hlog( LOG_DEBUG, "cellinit: %-12s block size %4d kB, cells/block: %d", arenaname, createkb, n ); // pthread_mutex_init(&ca->mutex, NULL); new_cellblock(ca); /* First block of cells, not yet need to be mutex protected */ while (ca->freecount < ca->minfree) new_cellblock(ca); /* more until minfree is full */ #if CELLHEAD_DEBUG == 1 // hlog(LOG_DEBUG, "cellinit() cellhead=%p", ca); #endif return ca; } inline void *cellhead_to_clientptr(struct cellhead *ch) { char *p = (char*)ch; #if CELLHEAD_DEBUG == 1 p += sizeof(void*); #endif return p; } inline struct cellhead *clientptr_to_cellhead(void *v) { #if CELLHEAD_DEBUG == 1 struct cellhead *ch = (struct cellhead *)(((char*)v) - sizeof(void*)); #else struct cellhead *ch = (struct cellhead*)v; #endif return ch; } void *cellmalloc(cellarena_t *ca) { void *cp; struct cellhead *ch; while (!ca->free_head || (ca->freecount < ca->minfree)) if (new_cellblock(ca)) { // pthread_mutex_unlock(&ca->mutex); return NULL; } /* Pick new one off the free-head ! */ ch = ca->free_head; ca->free_head = ch->next; ch->next = NULL; cp = ch; if (ca->free_head == NULL) ca->free_tail = NULL; ca->freecount -= 1; // hlog(LOG_DEBUG, "cellmalloc(%p at %p) freecount %d", cellhead_to_clientptr(cp), ca, ca->freecount); return cellhead_to_clientptr(cp); } /* * cellmallocmany() -- give many cells in single lock region * */ int cellmallocmany(cellarena_t *ca, void **array, int numcells) { int count; struct cellhead *ch; for (count = 0; count < numcells; ++count) { while (!ca->free_head || ca->freecount < ca->minfree) { /* Out of free cells ? alloc new set */ if (new_cellblock(ca)) { /* Failed ! */ break; } } /* Pick new one off the free-head ! */ ch = ca->free_head; // hlog( LOG_DEBUG, "cellmallocmany(%d of %d); freecount %d; %p at %p", // count, numcells, ca->freecount, cellhead_to_clientptr(ch), ca ); if (ch != NULL) { // should always be... ca->free_head = ch->next; ch->next = NULL; } if (ca->free_head == NULL) ca->free_tail = NULL; array[count] = cellhead_to_clientptr(ch); ca->freecount -= 1; } return count; } void cellfree(cellarena_t *ca, void *p) { struct cellhead *ch = clientptr_to_cellhead(p); ch->next = NULL; #if CELLHEAD_DEBUG == 1 if (ch->ca != ca) { // hlog(LOG_ERR, "cellfree(%p to %p) wrong cellhead->ca pointer %p", p, ca, ch->ca); } #endif // hlog(LOG_DEBUG, "cellfree() %p to %p", p, ca); if (ca->lifo_policy) { /* Put the cell on free-head */ ch->next = ca->free_head; ca->free_head = ch; } else { /* Put the cell on free-tail */ if (ca->free_tail) ca->free_tail->next = ch; ca->free_tail = ch; if (!ca->free_head) ca->free_head = ch; ch->next = NULL; } ca->freecount += 1; } /* * cellfreemany() -- release many cells in single lock region * */ void cellfreemany(cellarena_t *ca, void **array, int numcells) { int count; for (count = 0; count < numcells; ++count) { struct cellhead *ch = clientptr_to_cellhead(array[count]); #if CELLHEAD_DEBUG == 1 if (ch->ca != ca) { // hlog(LOG_ERR, "cellfreemany(%p to %p) wrong cellhead->ca pointer %p", array[count], ca, ch->ca); } #endif // hlog(LOG_DEBUG, "cellfreemany() %p to %p", ch, ca); if (ca->lifo_policy) { /* Put the cell on free-head */ ch->next = ca->free_head; ca->free_head = ch; } else { /* Put the cell on free-tail */ if (ca->free_tail) ca->free_tail->next = ch; ca->free_tail = ch; if (!ca->free_head) ca->free_head = ch; ch->next = NULL; } ca->freecount += 1; } } aprx-2.08.svn593/tt.53830000644000175000017500000076453212314021526013445 0ustar colincolinexecve("/bin/svn", ["svn", "--username", "oh2mqk", "commit", "doc/", "-m", "Add note about USB serial ports "...], [/* 59 vars */]) = 0 brk(0) = 0x7fb0c4d8b000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41f5000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=212134, ...}) = 0 mmap(NULL, 212134, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fb0c41c1000 close(3) = 0 open("/lib64/libsvn_client-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340\0a\2249\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=421472, ...}) = 0 mmap(NULL, 2508008, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c3d72000 mprotect(0x7fb0c3dd5000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c3fd4000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x62000) = 0x7fb0c3fd4000 close(3) = 0 open("/lib64/libsvn_wc-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0Z\301f8\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=741088, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41c0000 mmap(NULL, 2828136, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c3abf000 mprotect(0x7fb0c3b6f000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c3d6e000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xaf000) = 0x7fb0c3d6e000 close(3) = 0 open("/lib64/libsvn_ra-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260@ \2259\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=61752, ...}) = 0 mmap(NULL, 2151568, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c38b1000 mprotect(0x7fb0c38be000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c3abd000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xc000) = 0x7fb0c3abd000 close(3) = 0 open("/lib64/libsvn_diff-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340=\200f8\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=89768, ...}) = 0 mmap(NULL, 2180064, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c369c000 mprotect(0x7fb0c36b0000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c38af000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13000) = 0x7fb0c38af000 close(3) = 0 open("/lib64/libsvn_ra_local-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p4\240l8\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=44720, ...}) = 0 mmap(NULL, 2134952, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c3492000 mprotect(0x7fb0c349a000, 2097152, PROT_NONE) = 0 mmap(0x7fb0c369a000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x8000) = 0x7fb0c369a000 close(3) = 0 open("/lib64/libsvn_repos-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \227\300h8\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=223680, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41bf000 mmap(NULL, 2313200, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c325d000 mprotect(0x7fb0c3290000, 2097152, PROT_NONE) = 0 mmap(0x7fb0c3490000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x33000) = 0x7fb0c3490000 close(3) = 0 open("/lib64/libsvn_fs-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p:\0h8\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=48168, ...}) = 0 mmap(NULL, 2139032, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c3052000 mprotect(0x7fb0c305c000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c325b000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x9000) = 0x7fb0c325b000 close(3) = 0 open("/lib64/libsvn_fs_fs-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\240@g8\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=233360, ...}) = 0 mmap(NULL, 2322088, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c2e1b000 mprotect(0x7fb0c2e51000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c3050000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x35000) = 0x7fb0c3050000 close(3) = 0 open("/lib64/libsvn_fs_base-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360\226 m8\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=204032, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41be000 mmap(NULL, 2293192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c2beb000 mprotect(0x7fb0c2c1a000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c2e19000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2e000) = 0x7fb0c2e19000 close(3) = 0 open("/lib64/libsvn_fs_util-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200\f\0g8\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=14064, ...}) = 0 mmap(NULL, 2105520, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c29e8000 mprotect(0x7fb0c29ea000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c2be9000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x7fb0c2be9000 close(3) = 0 open("/lib64/libsvn_ra_svn-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360u@k8\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=132640, ...}) = 0 mmap(NULL, 2222232, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c27c9000 mprotect(0x7fb0c27e7000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c29e6000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1d000) = 0x7fb0c29e6000 close(3) = 0 open("/lib64/libsasl2.so.3", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`K\30001\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=122848, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41bd000 mmap(NULL, 2213960, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c25ac000 mprotect(0x7fb0c25c8000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c27c7000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b000) = 0x7fb0c27c7000 close(3) = 0 open("/lib64/libsvn_ra_serf-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\225`\2239\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=203848, ...}) = 0 mmap(NULL, 2292144, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c237c000 mprotect(0x7fb0c23a8000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c25a7000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2b000) = 0x7fb0c25a7000 close(3) = 0 open("/lib64/libserf-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P{\240\2239\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=115968, ...}) = 0 mmap(NULL, 2205248, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c2161000 mprotect(0x7fb0c217b000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c237a000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19000) = 0x7fb0c237a000 close(3) = 0 open("/lib64/libsvn_delta-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300<\340k8\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=85944, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41bc000 mmap(NULL, 2176072, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c1f4d000 mprotect(0x7fb0c1f5f000, 2097152, PROT_NONE) = 0 mmap(0x7fb0c215f000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x12000) = 0x7fb0c215f000 close(3) = 0 open("/lib64/libsvn_subr-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\266\341l8\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=523360, ...}) = 0 mmap(NULL, 2611640, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c1ccf000 mprotect(0x7fb0c1d48000, 2097152, PROT_NONE) = 0 mmap(0x7fb0c1f48000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x79000) = 0x7fb0c1f48000 close(3) = 0 open("/lib64/libz.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\"@\r1\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=92560, ...}) = 0 mmap(NULL, 2183688, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c1ab9000 mprotect(0x7fb0c1ace000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c1ccd000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x14000) = 0x7fb0c1ccd000 close(3) = 0 open("/lib64/libsqlite3.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\260\0k8\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=792288, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41bb000 mmap(NULL, 2873304, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c17fb000 mprotect(0x7fb0c18b4000, 2097152, PROT_NONE) = 0 mmap(0x7fb0c1ab4000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xb9000) = 0x7fb0c1ab4000 close(3) = 0 open("/lib64/libmagic.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`D\340\0321\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=126128, ...}) = 0 mmap(NULL, 2217848, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c15dd000 mprotect(0x7fb0c15f9000, 2097152, PROT_NONE) = 0 mmap(0x7fb0c17f9000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c000) = 0x7fb0c17f9000 close(3) = 0 open("/lib64/libaprutil-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@\232\340\0351\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=173416, ...}) = 0 mmap(NULL, 2263848, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c13b4000 mprotect(0x7fb0c13dc000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c15db000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x27000) = 0x7fb0c15db000 close(3) = 0 open("/lib64/libcrypt.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\16\200(1\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=43848, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41ba000 mmap(NULL, 2318912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c117d000 mprotect(0x7fb0c1185000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c1384000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x7000) = 0x7fb0c1384000 mmap(0x7fb0c1386000, 184896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb0c1386000 close(3) = 0 open("/lib64/libexpat.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0>@\0221\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=175384, ...}) = 0 mmap(NULL, 2265312, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c0f53000 mprotect(0x7fb0c0f7a000, 2097152, PROT_NONE) = 0 mmap(0x7fb0c117a000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x27000) = 0x7fb0c117a000 close(3) = 0 open("/lib64/libdb-5.3.so", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220\362\"\0341\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1840560, ...}) = 0 mmap(NULL, 3927304, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c0b94000 mprotect(0x7fb0c0d49000, 2097152, PROT_NONE) = 0 mmap(0x7fb0c0f49000, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b5000) = 0x7fb0c0f49000 close(3) = 0 open("/lib64/libapr-1.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \316\240\0361\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=219224, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41b9000 mmap(NULL, 2309880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c0960000 mprotect(0x7fb0c0992000, 2097152, PROT_NONE) = 0 mmap(0x7fb0c0b92000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x32000) = 0x7fb0c0b92000 close(3) = 0 open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340m\0\r1\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=150800, ...}) = 0 mmap(NULL, 2213104, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c0743000 mprotect(0x7fb0c075b000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c095a000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17000) = 0x7fb0c095a000 mmap(0x7fb0c095c000, 13552, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb0c095c000 close(3) = 0 open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\16\300\f1\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=22440, ...}) = 0 mmap(NULL, 2109744, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c053f000 mprotect(0x7fb0c0542000, 2093056, PROT_NONE) = 0 mmap(0x7fb0c0741000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7fb0c0741000 close(3) = 0 open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\36B\f1\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=2100672, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41b8000 mmap(NULL, 3924576, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0c0180000 mprotect(0x7fb0c0334000, 2097152, PROT_NONE) = 0 mmap(0x7fb0c0534000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b4000) = 0x7fb0c0534000 mmap(0x7fb0c053a000, 16992, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb0c053a000 close(3) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41b7000 open("/lib64/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@:\300\0161\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=113808, ...}) = 0 mmap(NULL, 2202264, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bff66000 mprotect(0x7fb0bff7c000, 2097152, PROT_NONE) = 0 mmap(0x7fb0c017c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16000) = 0x7fb0c017c000 mmap(0x7fb0c017e000, 6808, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb0c017e000 close(3) = 0 open("/usr/lib64/tls/x86_64/libssl.so.10", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/usr/lib64/tls/x86_64", 0x7fff60105170) = -1 ENOENT (No such file or directory) open("/usr/lib64/tls/libssl.so.10", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/usr/lib64/tls", {st_mode=S_IFDIR|0555, st_size=4096, ...}) = 0 open("/usr/lib64/x86_64/libssl.so.10", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/usr/lib64/x86_64", 0x7fff60105170) = -1 ENOENT (No such file or directory) open("/usr/lib64/libssl.so.10", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\203\1\0271\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=446080, ...}) = 0 mmap(NULL, 2536528, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bfcfa000 mprotect(0x7fb0bfd5c000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bff5b000, 45056, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x61000) = 0x7fb0bff5b000 close(3) = 0 open("/usr/lib64/tls/libcrypto.so.10", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libcrypto.so.10", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@\234F\0251\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1993248, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41b6000 mmap(NULL, 4091768, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bf913000 mprotect(0x7fb0bfad0000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bfccf000, 159744, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bc000) = 0x7fb0bfccf000 mmap(0x7fb0bfcf6000, 16248, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb0bfcf6000 close(3) = 0 open("/usr/lib64/tls/libldap_r-2.4.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libldap_r-2.4.so.2", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200\23\241\2229\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=365848, ...}) = 0 mmap(NULL, 2465032, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bf6b9000 mprotect(0x7fb0bf70e000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bf90d000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x54000) = 0x7fb0bf90d000 mmap(0x7fb0bf911000, 7432, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb0bf911000 close(3) = 0 open("/usr/lib64/tls/liblber-2.4.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/liblber-2.4.so.2", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\3206\30011\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=64280, ...}) = 0 mmap(NULL, 2155848, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bf4aa000 mprotect(0x7fb0bf4b8000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bf6b7000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xd000) = 0x7fb0bf6b7000 close(3) = 0 open("/usr/lib64/tls/libgssapi_krb5.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libgssapi_krb5.so.2", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\275@\0261\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=310624, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41b5000 mmap(NULL, 2398304, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bf260000 mprotect(0x7fb0bf2a7000, 2097152, PROT_NONE) = 0 mmap(0x7fb0bf4a7000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x47000) = 0x7fb0bf4a7000 close(3) = 0 open("/usr/lib64/tls/libkrb5.so.3", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libkrb5.so.3", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240Q\202\0261\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=929904, ...}) = 0 mmap(NULL, 3012704, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bef80000 mprotect(0x7fb0bf050000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bf24f000, 69632, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xcf000) = 0x7fb0bf24f000 close(3) = 0 open("/usr/lib64/tls/libk5crypto.so.3", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libk5crypto.so.3", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260H\0\0261\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=217248, ...}) = 0 mmap(NULL, 2310640, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bed4b000 mprotect(0x7fb0bed7d000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bef7c000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x31000) = 0x7fb0bef7c000 mmap(0x7fb0bef7f000, 496, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb0bef7f000 close(3) = 0 open("/usr/lib64/tls/libcom_err.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libcom_err.so.2", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\25\0\0251\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=18320, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41b4000 mmap(NULL, 2109928, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0beb47000 mprotect(0x7fb0beb4a000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bed49000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7fb0bed49000 close(3) = 0 open("/lib64/libuuid.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\25\300\0201\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=22624, ...}) = 0 mmap(NULL, 2113920, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0be942000 mprotect(0x7fb0be946000, 2093056, PROT_NONE) = 0 mmap(0x7fb0beb45000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7fb0beb45000 close(3) = 0 open("/lib64/libfreebl3.so", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200>\200'1\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=517384, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41b3000 mmap(NULL, 2619232, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0be6c2000 mprotect(0x7fb0be73c000, 2093056, PROT_NONE) = 0 mmap(0x7fb0be93b000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x79000) = 0x7fb0be93b000 mmap(0x7fb0be93e000, 14176, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb0be93e000 close(3) = 0 open("/usr/lib64/tls/libssl3.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libssl3.so", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220\260`\2219\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=262984, ...}) = 0 mmap(NULL, 2351976, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0be483000 mprotect(0x7fb0be4be000, 2093056, PROT_NONE) = 0 mmap(0x7fb0be6bd000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3a000) = 0x7fb0be6bd000 mmap(0x7fb0be6c1000, 872, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb0be6c1000 close(3) = 0 open("/usr/lib64/tls/libsmime3.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libsmime3.so", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\236\240\2219\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=192376, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41b2000 mmap(NULL, 2280576, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0be256000 mprotect(0x7fb0be27f000, 2093056, PROT_NONE) = 0 mmap(0x7fb0be47e000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7fb0be47e000 close(3) = 0 open("/usr/lib64/tls/libnss3.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libnss3.so", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\232\341\2209\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1360352, ...}) = 0 mmap(NULL, 3434440, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bdf0f000 mprotect(0x7fb0be04e000, 2093056, PROT_NONE) = 0 mmap(0x7fb0be24d000, 32768, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13e000) = 0x7fb0be24d000 mmap(0x7fb0be255000, 1992, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb0be255000 close(3) = 0 open("/usr/lib64/tls/libnssutil3.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libnssutil3.so", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\276 \2219\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=184296, ...}) = 0 mmap(NULL, 2275936, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bdce3000 mprotect(0x7fb0bdd08000, 2097152, PROT_NONE) = 0 mmap(0x7fb0bdf08000, 28672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7fb0bdf08000 close(3) = 0 open("/usr/lib64/tls/libplds4.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libplds4.so", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220\20`\2209\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=18168, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41b1000 mmap(NULL, 2109800, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bdadf000 mprotect(0x7fb0bdae2000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bdce1000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7fb0bdce1000 close(3) = 0 open("/usr/lib64/tls/libplc4.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libplc4.so", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\25\240\2209\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=22272, ...}) = 0 mmap(NULL, 2113936, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bd8da000 mprotect(0x7fb0bd8de000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bdadd000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7fb0bdadd000 close(3) = 0 open("/usr/lib64/tls/libnspr4.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libnspr4.so", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340\321 \2209\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=251552, ...}) = 0 mmap(NULL, 2350496, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bd69c000 mprotect(0x7fb0bd6d6000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bd8d5000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x39000) = 0x7fb0bd8d5000 mmap(0x7fb0bd8d8000, 7584, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb0bd8d8000 close(3) = 0 open("/usr/lib64/tls/libkrb5support.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libkrb5support.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 6\300\0261\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=60896, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41b0000 mmap(NULL, 2152008, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bd48e000 mprotect(0x7fb0bd49b000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bd69a000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xc000) = 0x7fb0bd69a000 close(3) = 0 open("/usr/lib64/tls/libkeyutils.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libkeyutils.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\25\200\0251\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=17984, ...}) = 0 mmap(NULL, 2109712, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bd28a000 mprotect(0x7fb0bd28d000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bd48c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7fb0bd48c000 close(3) = 0 open("/usr/lib64/tls/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/librt.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\"\200\0161\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=47400, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41af000 mmap(NULL, 2128952, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bd082000 mprotect(0x7fb0bd089000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bd288000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7fb0bd288000 close(3) = 0 open("/usr/lib64/tls/libselinux.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240d@\0161\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=144952, ...}) = 0 mmap(NULL, 2242712, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bce5e000 mprotect(0x7fb0bce7f000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bd07e000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x20000) = 0x7fb0bd07e000 mmap(0x7fb0bd080000, 6296, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb0bd080000 close(3) = 0 open("/usr/lib64/tls/libpcre.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\27\0\0161\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=421144, ...}) = 0 mmap(NULL, 2511368, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bcbf8000 mprotect(0x7fb0bcc5d000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bce5c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x64000) = 0x7fb0bce5c000 close(3) = 0 open("/usr/lib64/tls/liblzma.so.5", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/liblzma.so.5", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\3400\300\r1\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=155400, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41ae000 mmap(NULL, 2245240, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb0bc9d3000 mprotect(0x7fb0bc9f7000, 2093056, PROT_NONE) = 0 mmap(0x7fb0bcbf6000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x23000) = 0x7fb0bcbf6000 close(3) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41ad000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41ac000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41ab000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41aa000 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41a8000 arch_prctl(ARCH_SET_FS, 0x7fb0c41a8880) = 0 mprotect(0x7fb0c0534000, 16384, PROT_READ) = 0 mprotect(0x7fb0c095a000, 4096, PROT_READ) = 0 mprotect(0x7fb0bcbf6000, 4096, PROT_READ) = 0 mprotect(0x7fb0bce5c000, 4096, PROT_READ) = 0 mprotect(0x7fb0c0741000, 4096, PROT_READ) = 0 mprotect(0x7fb0bd07e000, 4096, PROT_READ) = 0 mprotect(0x7fb0bd288000, 4096, PROT_READ) = 0 mprotect(0x7fb0bd48c000, 4096, PROT_READ) = 0 mprotect(0x7fb0c017c000, 4096, PROT_READ) = 0 mprotect(0x7fb0bd69a000, 4096, PROT_READ) = 0 mprotect(0x7fb0bd8d5000, 4096, PROT_READ) = 0 mprotect(0x7fb0bdadd000, 4096, PROT_READ) = 0 mprotect(0x7fb0bdce1000, 4096, PROT_READ) = 0 mprotect(0x7fb0bdf08000, 24576, PROT_READ) = 0 mprotect(0x7fb0be24d000, 20480, PROT_READ) = 0 mprotect(0x7fb0be47e000, 16384, PROT_READ) = 0 mprotect(0x7fb0c1ccd000, 4096, PROT_READ) = 0 mprotect(0x7fb0be6bd000, 12288, PROT_READ) = 0 mprotect(0x7fb0be93b000, 8192, PROT_READ) = 0 mprotect(0x7fb0beb45000, 4096, PROT_READ) = 0 mprotect(0x7fb0bed49000, 4096, PROT_READ) = 0 mprotect(0x7fb0bef7c000, 8192, PROT_READ) = 0 mprotect(0x7fb0bf24f000, 57344, PROT_READ) = 0 mprotect(0x7fb0bf4a7000, 4096, PROT_READ) = 0 mprotect(0x7fb0bf6b7000, 4096, PROT_READ) = 0 mprotect(0x7fb0c1384000, 4096, PROT_READ) = 0 mprotect(0x7fb0c27c7000, 4096, PROT_READ) = 0 mprotect(0x7fb0bf90d000, 8192, PROT_READ) = 0 mprotect(0x7fb0bfccf000, 110592, PROT_READ) = 0 mprotect(0x7fb0bff5b000, 16384, PROT_READ) = 0 mprotect(0x7fb0c0b92000, 4096, PROT_READ) = 0 mprotect(0x7fb0c0f49000, 28672, PROT_READ) = 0 mprotect(0x7fb0c117a000, 8192, PROT_READ) = 0 mprotect(0x7fb0c15db000, 4096, PROT_READ) = 0 mprotect(0x7fb0c17f9000, 4096, PROT_READ) = 0 mprotect(0x7fb0c1ab4000, 8192, PROT_READ) = 0 mprotect(0x7fb0c1f48000, 12288, PROT_READ) = 0 mprotect(0x7fb0c215f000, 4096, PROT_READ) = 0 mprotect(0x7fb0c237a000, 4096, PROT_READ) = 0 mprotect(0x7fb0c25a7000, 16384, PROT_READ) = 0 mprotect(0x7fb0c29e6000, 4096, PROT_READ) = 0 mprotect(0x7fb0c2be9000, 4096, PROT_READ) = 0 mprotect(0x7fb0c2e19000, 4096, PROT_READ) = 0 mprotect(0x7fb0c3050000, 4096, PROT_READ) = 0 mprotect(0x7fb0c325b000, 4096, PROT_READ) = 0 mprotect(0x7fb0c3490000, 4096, PROT_READ) = 0 mprotect(0x7fb0c369a000, 4096, PROT_READ) = 0 mprotect(0x7fb0c38af000, 4096, PROT_READ) = 0 mprotect(0x7fb0c3abd000, 4096, PROT_READ) = 0 mprotect(0x7fb0c3d6e000, 8192, PROT_READ) = 0 mprotect(0x7fb0c3fd4000, 4096, PROT_READ) = 0 mprotect(0x7fb0c4431000, 49152, PROT_READ) = 0 mprotect(0x7fb0c41f6000, 4096, PROT_READ) = 0 munmap(0x7fb0c41c1000, 212134) = 0 set_tid_address(0x7fb0c41a8b50) = 5383 set_robust_list(0x7fb0c41a8b60, 24) = 0 rt_sigaction(SIGRTMIN, {0x7fb0c07498c0, [], SA_RESTORER|SA_SIGINFO, 0x7fb0c0752750}, NULL, 8) = 0 rt_sigaction(SIGRT_1, {0x7fb0c0749950, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x7fb0c0752750}, NULL, 8) = 0 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0 getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0 statfs("/sys/fs/selinux", 0x7fff60106060) = -1 ENOENT (No such file or directory) statfs("/selinux", 0x7fff60106060) = -1 ENOENT (No such file or directory) brk(0) = 0x7fb0c4d8b000 brk(0x7fb0c4dac000) = 0x7fb0c4dac000 open("/proc/filesystems", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41f4000 read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tr"..., 1024) = 384 read(3, "", 1024) = 0 close(3) = 0 munmap(0x7fb0c41f4000, 4096) = 0 access("/etc/system-fips", F_OK) = -1 ENOENT (No such file or directory) fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0 fstat(2, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0 open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=106070960, ...}) = 0 mmap(NULL, 106070960, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fb0b64aa000 close(3) = 0 open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=2492, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41f4000 read(3, "# Locale name alias data base.\n#"..., 4096) = 2492 read(3, "", 4096) = 0 close(3) = 0 munmap(0x7fb0c41f4000, 4096) = 0 open("/usr/lib/locale/A4/LC_PAPER", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) ioctl(0, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) = 0 stat("/localhome/mea/.subversion", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/localhome/mea/.subversion/auth", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0 lstat("/localhome/mea/.subversion/auth/svn.simple", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/localhome/mea/.subversion/auth/svn.username", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/localhome/mea/.subversion/auth/svn.ssl.server", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/localhome/mea/.subversion/auth/svn.ssl.client-passphrase", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0 lstat("/localhome/mea/.subversion/README.txt", {st_mode=S_IFREG|0644, st_size=4277, ...}) = 0 lstat("/localhome/mea/.subversion/servers", {st_mode=S_IFREG|0644, st_size=3270, ...}) = 0 lstat("/localhome/mea/.subversion/config", {st_mode=S_IFREG|0664, st_size=4749, ...}) = 0 open("/etc/subversion/servers", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/localhome/mea/.subversion/servers", O_RDONLY|O_CLOEXEC) = 3 fcntl(3, F_GETFD) = 0x1 (flags FD_CLOEXEC) brk(0) = 0x7fb0c4dac000 brk(0x7fb0c4ddc000) = 0x7fb0c4ddc000 read(3, "### This file specifies server-s"..., 4096) = 3270 read(3, "", 4096) = 0 read(3, "", 4096) = 0 read(3, "", 4096) = 0 close(3) = 0 open("/etc/subversion/config", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/localhome/mea/.subversion/config", O_RDONLY|O_CLOEXEC) = 3 read(3, "### This file configures various"..., 4096) = 4096 read(3, " is:\n### file-name-pattern = p"..., 4096) = 653 read(3, "", 4096) = 0 read(3, "", 4096) = 0 read(3, "", 4096) = 0 close(3) = 0 stat("Add note about USB serial ports benefitting of interface timeout parameter.", 0x7fff60105ca0) = -1 ENOENT (No such file or directory) getcwd("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk", 4096) = 77 rt_sigaction(SIGINT, {0x7fb0c4216ad0, [], SA_RESTORER|SA_INTERRUPT, 0x7fb0c0752750}, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGHUP, {0x7fb0c4216ad0, [], SA_RESTORER|SA_INTERRUPT, 0x7fb0c0752750}, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGTERM, {0x7fb0c4216ad0, [], SA_RESTORER|SA_INTERRUPT, 0x7fb0c0752750}, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGPIPE, {SIG_IGN, [], SA_RESTORER|SA_INTERRUPT, 0x7fb0c0752750}, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGXFSZ, {SIG_IGN, [], SA_RESTORER|SA_INTERRUPT, 0x7fb0c0752750}, {SIG_DFL, [], 0}, 8) = 0 getcwd("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk", 4096) = 77 getcwd("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk", 4096) = 77 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/.svn", 0x7fff60105720) = -1 ENOENT (No such file or directory) lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn", {st_mode=S_IFDIR|0755, st_size=43, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", O_RDWR|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 0, SEEK_SET) = 0 read(3, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\237\0\0\0\241"..., 100) = 100 brk(0) = 0x7fb0c4ddc000 brk(0x7fb0c4e09000) = 0x7fb0c4e09000 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 0, SEEK_SET) = 0 read(3, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\237\0\0\0\241"..., 1024) = 1024 lseek(3, 11264, SEEK_SET) = 11264 read(3, "\r\0\0\0\t\0x\0\0x\1\25\1J\1\234\1\342\2(\2\242\2\317\0030\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 14336, SEEK_SET) = 14336 read(3, "\r\3+\0\3\0\204\0\0\204\0\265\2\364\3+\3\227\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 19456, SEEK_SET) = 19456 read(3, "\r\0\0\0\5\0017\0\0017\2D\2m\2\354\3\321\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 25600, SEEK_SET) = 25600 read(3, "\r\3\230\0\2\0\233\0\0\233\3m\3\230\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 27648, SEEK_SET) = 27648 read(3, "\r\0\0\0\4\0\335\0\0\335\1\347\2L\3&\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 28672, SEEK_SET) = 28672 read(3, "\r\0\0\0\2\0\330\0\0\330\2'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 33792, SEEK_SET) = 33792 read(3, "\r\2\234\0\6\0E\0\2i\3\n\2\2\1\250\0\374\0E\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\237\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 8192, SEEK_SET) = 8192 read(3, "\n\0\0\0\1\3\374\0\3\374\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\237\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\237\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 20480, SEEK_SET) = 20480 read(3, "\r\0\0\0\0\4\0\0\2\230\2\230\2\230\2\230\2\230\2\230\2\230\2\230\2\230\1\232\1\232\1\232"..., 1024) = 1024 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\237\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24576, SEEK_SET) = 24576 read(3, "\2\0\0\0\2\3\315\0\0\0\0y\3\342\3\315\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 53248, SEEK_SET) = 53248 read(3, "\n\3\226\0\35\0012\2\1d\1|\1\233\0012\1\304\1\333\1\356\2\0\2\33\1\260\2B\2M"..., 1024) = 1024 lseek(3, 23552, SEEK_SET) = 23552 read(3, "\5\3~\0$\3\25\f\0\0\0\241\3\265\3\260\3L\3X\3\246\3\241\3\234\3\227\3\222\3\210"..., 1024) = 1024 lseek(3, 148480, SEEK_SET) = 148480 read(3, "\r\0\0\0\2\2\202\0\2\202\3\17\3\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 52224, SEEK_SET) = 52224 read(3, "\n\2\240\0%\1+\0\1G\1O\1`\1o\1~\1\221\1\241\1\257\1\275\1\327\1\343\1\362"..., 1024) = 1024 lseek(3, 163840, SEEK_SET) = 163840 read(3, "\r\3(\0\2\1\301\0\1\301\2\244\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 getcwd("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk", 4096) = 77 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\237\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 22528, SEEK_SET) = 22528 read(3, "\n\0\0\0\0\4\0\0\3\373\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741825, len=1}) = 0 lseek(3, 21504, SEEK_SET) = 21504 read(3, "\r\0\0\0\0\4\0\0\3\371\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", O_RDWR|O_CREAT|O_CLOEXEC, 0644) = 4 fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 geteuid() = 530 fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 open("/dev/urandom", O_RDONLY|O_CLOEXEC) = 5 read(5, "!?\\6X$\260\376\v\3335\213\17H\267\303\0076eP\206\3N\264\320\311K\335\241vF\331"..., 256) = 256 close(5) = 0 lseek(4, 0, SEEK_SET) = 0 write(4, "\331\325\5\371 \241c\327\377\377\377\377\224\337\311\2\0\0\0\241\0\0\2\0\0\0\4\0\0\0\0\0"..., 512) = 512 lseek(4, 512, SEEK_SET) = 512 write(4, "\0\0\0\27", 4) = 4 lseek(4, 516, SEEK_SET) = 516 write(4, "\n\0\0\0\0\4\0\0\3\373\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(4, 1540, SEEK_SET) = 1540 write(4, "\224\337\311\2", 4) = 4 lseek(4, 1544, SEEK_SET) = 1544 write(4, "\0\0\0\26", 4) = 4 lseek(4, 1548, SEEK_SET) = 1548 write(4, "\r\0\0\0\0\4\0\0\3\371\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(4, 2572, SEEK_SET) = 2572 write(4, "\224\337\311\2", 4) = 4 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 lseek(4, 2576, SEEK_SET) = 2576 write(4, "\0\0\0\1", 4) = 4 lseek(4, 2580, SEEK_SET) = 2580 write(4, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\237\0\0\0\241"..., 1024) = 1024 lseek(4, 3604, SEEK_SET) = 3604 write(4, "\224\337\311\2", 4) = 4 lseek(3, 0, SEEK_SET) = 0 write(3, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\240\0\0\0\241"..., 1024) = 1024 lseek(3, 21504, SEEK_SET) = 21504 write(3, "\r\0\0\0\1\3\371\0\3\371\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 22528, SEEK_SET) = 22528 write(3, "\n\0\0\0\1\3\373\0\3\373\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 close(4) = 0 unlink("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal") = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=2}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 getcwd("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk", 4096) = 77 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 13312, SEEK_SET) = 13312 read(3, "\n\0\0\0\0\4\0\0\3\365\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 18432, SEEK_SET) = 18432 read(3, "\n\0\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 1024, SEEK_SET) = 1024 read(3, "\r\0\0\0\1\3\273\0\3\273\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 brk(0) = 0x7fb0c4e09000 brk(0x7fb0c4e2a000) = 0x7fb0c4e2a000 brk(0) = 0x7fb0c4e2a000 brk(0) = 0x7fb0c4e2a000 brk(0x7fb0c4e29000) = 0x7fb0c4e29000 brk(0) = 0x7fb0c4e29000 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 32768, SEEK_SET) = 32768 read(3, "\n\0\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 openat(AT_FDCWD, "/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 4 getdents(4, /* 8 entries */, 32768) = 328 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/..", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual-pics.odp", {st_mode=S_IFREG|0664, st_size=12644, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.odt", {st_mode=S_IFREG|0664, st_size=197896, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-requirement-specification.odt", {st_mode=S_IFREG|0644, st_size=34162, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.pdf", {st_mode=S_IFREG|0664, st_size=438348, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-requirement-specification.pdf", {st_mode=S_IFREG|0644, st_size=175248, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/.~lock.aprx-manual.odt#", {st_mode=S_IFREG|0664, st_size=95, ...}) = 0 getdents(4, /* 0 entries */, 32768) = 0 close(4) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 26624, SEEK_SET) = 26624 read(3, "\2\3\360\0\2\3\277\0\0\0\0t\3\277\3\322\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 117760, SEEK_SET) = 117760 read(3, "\n\0\0\0\21\1\265\0\1\265\1\316\1\360\2\n\2-\2L\2k\2\234\2\315\2\357\3\16\0030"..., 1024) = 1024 lseek(3, 68608, SEEK_SET) = 68608 read(3, "\r\0\0\0\4\0\23\0\2\273\2\"\1\2\0\23\0\0\0\201lO\31\t+\10\35\t7\2\31\0"..., 1024) = 1024 lseek(3, 105472, SEEK_SET) = 105472 read(3, "\r\0\0\0\3\0\311\0\0\311\1\341\2\350\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 64512, SEEK_SET) = 64512 read(3, "\r\0\0\0\3\0e\0\0e\2\250\1P\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 15360, SEEK_SET) = 15360 read(3, "\n\0\0\0\0\4\0\0\3\364\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.odt", {st_mode=S_IFREG|0664, st_size=197896, ...}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.odt", {st_mode=S_IFREG|0664, st_size=197896, ...}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 10240, SEEK_SET) = 10240 read(3, "\2\0\0\0\1\3\310\0\0\0\0\213\3\310\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 140288, SEEK_SET) = 140288 read(3, "\2\1\325\0\f\1-\2\0\0\0|\2B\2\n\2\352\1-\2z\1e\2\263\3\311\3X\3!"..., 1024) = 1024 lseek(3, 88064, SEEK_SET) = 88064 read(3, "\n\0\0\0\16\1/\0\1/\1b\1\225\1\311\1\375\0020\2c\2\227\2\312\2\376\0031\3e"..., 1024) = 1024 lseek(3, 9216, SEEK_SET) = 9216 read(3, "\5\0\0\0&\3(\0\0\0\0\240\3\373\3\366\3\361\3\354\3\347\3\342\3\335\3\330\3\323\3\316"..., 1024) = 1024 lseek(3, 150528, SEEK_SET) = 150528 read(3, "\r\0\0\0\n\0F\0\3\241\3A\2\341\2\202\2\"\1\303\1d\1\5\0\245\0F\0\0\0\0"..., 1024) = 1024 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/pristine/5b/5b78dd1d69e60a7ae82aec254bcc346289c7895d.svn-base", O_RDONLY|O_CLOEXEC) = 4 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 close(4) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.pdf", {st_mode=S_IFREG|0664, st_size=438348, ...}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.pdf", {st_mode=S_IFREG|0664, st_size=438348, ...}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 98304, SEEK_SET) = 98304 read(3, "\n\0\0\0\17\0\367\0\1+\1_\1\223\1\307\1\373\2.\2b\2\226\2\311\2\374\0\367\0030"..., 1024) = 1024 lseek(3, 145408, SEEK_SET) = 145408 read(3, "\r\0\0\0\n\0F\0\3\241\3A\2\342\2\203\2#\1\303\1d\1\5\0\246\0F\0\0\0\0"..., 1024) = 1024 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/pristine/80/801e13de8f6f628e0af791392a3cdbe991e46277.svn-base", O_RDONLY|O_CLOEXEC) = 4 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 close(4) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 epoll_create1(EPOLL_CLOEXEC) = 4 socket(PF_NETLINK, SOCK_RAW, 0) = 5 bind(5, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 0 getsockname(5, {sa_family=AF_NETLINK, pid=5383, groups=00000000}, [12]) = 0 sendto(5, "\24\0\0\0\26\0\1\3(#0S\0\0\0\0\0\0\0\0", 20, 0, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 20 recvmsg(5, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"D\0\0\0\24\0\2\0(#0S\7\25\0\0\2\10\200\376\1\0\0\0\10\0\1\0\177\0\0\1"..., 4096}], msg_controllen=0, msg_flags=0}, 0) = 148 recvmsg(5, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"H\0\0\0\24\0\2\0(#0S\7\25\0\0\n\200\200\376\1\0\0\0\24\0\1\0\0\0\0\0"..., 4096}], msg_controllen=0, msg_flags=0}, 0) = 216 recvmsg(5, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"\24\0\0\0\3\0\2\0(#0S\7\25\0\0\0\0\0\0", 4096}], msg_controllen=0, msg_flags=0}, 0) = 20 socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 6 connect(6, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory) close(6) = 0 close(5) = 0 socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 5 connect(5, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory) close(5) = 0 open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 5 fstat(5, {st_mode=S_IFREG|0644, st_size=1751, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41f4000 read(5, "#\n# /etc/nsswitch.conf\n#\n# An ex"..., 4096) = 1751 read(5, "", 4096) = 0 close(5) = 0 munmap(0x7fb0c41f4000, 4096) = 0 open("/etc/host.conf", O_RDONLY|O_CLOEXEC) = 5 fstat(5, {st_mode=S_IFREG|0644, st_size=9, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41f4000 read(5, "multi on\n", 4096) = 9 read(5, "", 4096) = 0 close(5) = 0 munmap(0x7fb0c41f4000, 4096) = 0 futex(0x7fb0c053d3d0, FUTEX_WAKE_PRIVATE, 2147483647) = 0 open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 5 fstat(5, {st_mode=S_IFREG|0644, st_size=94, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41f4000 read(5, "# Generated by NetworkManager\nse"..., 4096) = 94 read(5, "", 4096) = 0 close(5) = 0 munmap(0x7fb0c41f4000, 4096) = 0 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 5 fstat(5, {st_mode=S_IFREG|0644, st_size=212134, ...}) = 0 mmap(NULL, 212134, PROT_READ, MAP_PRIVATE, 5, 0) = 0x7fb0c41c1000 close(5) = 0 open("/lib64/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 5 read(5, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@\"\0\0\0\0\0\0"..., 832) = 832 fstat(5, {st_mode=S_IFREG|0755, st_size=57976, ...}) = 0 mmap(NULL, 2144360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 5, 0) = 0x7fb0b629e000 mprotect(0x7fb0b62a9000, 2093056, PROT_NONE) = 0 mmap(0x7fb0b64a8000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 5, 0xa000) = 0x7fb0b64a8000 close(5) = 0 mprotect(0x7fb0b64a8000, 4096, PROT_READ) = 0 munmap(0x7fb0c41c1000, 212134) = 0 open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 5 fstat(5, {st_mode=S_IFREG|0644, st_size=210, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41f4000 read(5, "127.0.0.1 localhost localhost."..., 4096) = 210 read(5, "", 4096) = 0 close(5) = 0 munmap(0x7fb0c41f4000, 4096) = 0 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 5 fstat(5, {st_mode=S_IFREG|0644, st_size=212134, ...}) = 0 mmap(NULL, 212134, PROT_READ, MAP_PRIVATE, 5, 0) = 0x7fb0c41c1000 close(5) = 0 open("/lib64/libnss_dns.so.2", O_RDONLY|O_CLOEXEC) = 5 read(5, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\21\0\0\0\0\0\0"..., 832) = 832 fstat(5, {st_mode=S_IFREG|0755, st_size=27512, ...}) = 0 mmap(NULL, 2117888, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 5, 0) = 0x7fb0b6098000 mprotect(0x7fb0b609d000, 2093056, PROT_NONE) = 0 mmap(0x7fb0b629c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 5, 0x4000) = 0x7fb0b629c000 close(5) = 0 mprotect(0x7fb0b629c000, 4096, PROT_READ) = 0 munmap(0x7fb0c41c1000, 212134) = 0 socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 5 connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.10.10.53")}, 16) = 0 poll([{fd=5, events=POLLOUT}], 1, 0) = 1 ([{fd=5, revents=POLLOUT}]) sendmmsg(5, {{{msg_name(0)=NULL, msg_iov(1)=[{"\36\326\1\0\0\1\0\0\0\0\0\0\4repo\3ham\2fi\0\0\1\0\1", 29}], msg_controllen=0, msg_flags=0}, 29}, {{msg_name(0)=NULL, msg_iov(1)=[{"\2125\1\0\0\1\0\0\0\0\0\0\4repo\3ham\2fi\0\0\34\0\1", 29}], msg_controllen=0, msg_flags=0}, 29}}, 2, MSG_NOSIGNAL) = 2 poll([{fd=5, events=POLLIN}], 1, 5000) = 1 ([{fd=5, revents=POLLIN}]) ioctl(5, FIONREAD, [508]) = 0 recvfrom(5, "\36\326\201\200\0\1\0\1\0\r\0\f\4repo\3ham\2fi\0\0\1\0\1\300\f\0"..., 2048, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.10.10.53")}, [16]) = 508 poll([{fd=5, events=POLLIN}], 1, 4998) = 1 ([{fd=5, revents=POLLIN}]) ioctl(5, FIONREAD, [99]) = 0 recvfrom(5, "\2125\201\200\0\1\0\0\0\1\0\0\4repo\3ham\2fi\0\0\34\0\1\300\21\0"..., 1540, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.10.10.53")}, [16]) = 99 close(5) = 0 brk(0) = 0x7fb0c4e29000 brk(0x7fb0c4e4b000) = 0x7fb0c4e4b000 socket(PF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_TCP) = 5 fcntl(5, F_GETFL) = 0x2 (flags O_RDWR) fcntl(5, F_SETFL, O_RDWR|O_NONBLOCK) = 0 setsockopt(5, SOL_TCP, TCP_NODELAY, [1], 4) = 0 connect(5, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("193.19.136.46")}, 16) = -1 EINPROGRESS (Operation now in progress) epoll_ctl(4, EPOLL_CTL_DEL, 5, {0, {u32=0, u64=0}}) = -1 ENOENT (No such file or directory) epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLOUT, {u32=3303153608, u64=140397194125256}}) = 0 epoll_wait(4, {{EPOLLOUT, {u32=3303153608, u64=140397194125256}}}, 16, 500) = 1 writev(5, [{"OPTIONS /svn/aprx/trunk/doc HTTP"..., 38}, {"Host", 4}, {": ", 2}, {"repo.ham.fi", 11}, {"\r\n", 2}, {"User-Agent", 10}, {": ", 2}, {"SVN/1.8.8 (x86_64-redhat-linux-g"..., 46}, {"\r\n", 2}, {"Content-Type", 12}, {": ", 2}, {"text/xml", 8}, {"\r\n", 2}, {"Connection", 10}, {": ", 2}, {"keep-alive", 10}, {"\r\n", 2}, {"Accept-Encoding", 15}, {": ", 2}, {"gzip", 4}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 48}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 52}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 55}, ...], 39) = 518 epoll_ctl(4, EPOLL_CTL_DEL, 5, {0, {u32=0, u64=0}}) = 0 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN, {u32=3303153656, u64=140397194125304}}) = 0 epoll_wait(4, {{EPOLLIN, {u32=3303153656, u64=140397194125304}}}, 16, 500) = 1 read(5, "HTTP/1.1 200 OK\r\nDate: Mon, 24 M"..., 8000) = 860 brk(0) = 0x7fb0c4e4b000 brk(0x7fb0c4e6c000) = 0x7fb0c4e6c000 epoll_ctl(4, EPOLL_CTL_DEL, 5, {0, {u32=0, u64=0}}) = 0 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLOUT, {u32=3303153608, u64=140397194125256}}) = 0 epoll_wait(4, {{EPOLLOUT, {u32=3303153608, u64=140397194125256}}}, 16, 500) = 1 writev(5, [{"OPTIONS /svn/aprx/trunk/doc HTTP"..., 38}, {"Host", 4}, {": ", 2}, {"repo.ham.fi", 11}, {"\r\n", 2}, {"User-Agent", 10}, {": ", 2}, {"SVN/1.8.8 (x86_64-redhat-linux-g"..., 46}, {"\r\n", 2}, {"Accept-Encoding", 15}, {": ", 2}, {"gzip", 4}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 48}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 52}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 55}, {"\r\n", 2}, {"Transfer-Encoding", 17}, {": ", 2}, {"chunked", 7}, {"\r\n", 2}, {"\r\n", 2}, {"42\r\n", 4}, {"\24\200\223\260\26\336#!\266\4\360\347\24\330"..., 4096) = 4096 read(6, "Y\16f\211\1\362\242\364\v(\361\274\370\261G\"\27}\3006\337\234(l\235\3057*\177+T~"..., 4096) = 4096 read(6, "\201\205\37\340\265\245Dm \3366b\323\0025\346,?%\376\262\372]PJXSN\356t\34{"..., 4096) = 4096 read(6, "\35\204T\20\4\377\366\351z\306\334\217n3<\334\34\351\241\207\252C\221\f\213\3618AG\"\256\4"..., 4096) = 4096 read(6, "\0103\\\275s\304\274C\5s0\366\7\327\224\20\3k\310\224\220\365%r\356F,3I\237'\211"..., 4096) = 4096 read(6, "p\256\24\230\203\354\275\306\1b0\201\202\251\23\35f\333\216\341\7\2V2\305\352\310!E\347\211\327"..., 4096) = 4096 read(6, "\204\311\212\307^\"2\2752\310\255AO\337\304\370\262\232H\336et\343\23q\300\375\220\0027\30\212"..., 4096) = 4096 read(6, "N\260\372\330\3222RQQ\271\34x~\213\317\206\305\313V\304F\204\23B\250\377\377\265\320\236]b"..., 4096) = 4096 read(6, "\214\355\321\313\214\376\312\312nd\350\275{eee\1\27\2\355\306\214Ul;\260\233b\357\201\203\273"..., 4096) = 4096 write(7, "PK\3\4\24\0\0\10\0\0\360axD^\3062\f'\0\0\0'\0\0\0\10\0\0\0mi"..., 4096) = 4096 write(7, "\350\305\345\33_GTU\377|\203\225\344\344\344\204B\373A\21\233\364\330\244\322J\360\206t\302\237v"..., 4096) = 4096 write(7, "\34\316f\263\234U\304\337Q\230\3019\317d\261K+\336\311\347<\223\v\315@y'\237\363L\26\32"..., 4096) = 4096 write(7, "\27\17\321\36\314\242\213\207h\23\222\321\305C\264\7\337\350\342!Z\205z\224\214\207\230u\361\20\r\303"..., 4096) = 4096 write(7, "\326\252\234\370@\373#{\274\367\3\233\364\365\264;\35U\327C^\346\20\357\207\37\t=\310\216\256\34"..., 4096) = 4096 write(7, "Qx\320\26<\260E\264\200\2\364\302\335# \346h>\24\200\223\260\26\336#!\266\4\360\347\24\330"..., 4096) = 4096 write(7, "Y\16f\211\1\362\242\364\v(\361\274\370\261G\"\27}\3006\337\234(l\235\3057*\177+T~"..., 4096) = 4096 write(7, "\201\205\37\340\265\245Dm \3366b\323\0025\346,?%\376\262\372]PJXSN\356t\34{"..., 4096) = 4096 write(7, "\35\204T\20\4\377\366\351z\306\334\217n3<\334\34\351\241\207\252C\221\f\213\3618AG\"\256\4"..., 4096) = 4096 write(7, "\0103\\\275s\304\274C\5s0\366\7\327\224\20\3k\310\224\220\365%r\356F,3I\237'\211"..., 4096) = 4096 write(7, "p\256\24\230\203\354\275\306\1b0\201\202\251\23\35f\333\216\341\7\2V2\305\352\310!E\347\211\327"..., 4096) = 4096 write(7, "\204\311\212\307^\"2\2752\310\255AO\337\304\370\262\232H\336et\343\23q\300\375\220\0027\30\212"..., 4096) = 4096 write(7, "N\260\372\330\3222RQQ\271\34x~\213\317\206\305\313V\304F\204\23B\250\377\377\265\320\236]b"..., 4096) = 4096 mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c41d2000 write(9, "SVN\0\0\206\240\0\206\240\0\6\206\237v\n\0\200\206\237v\360axD^\3062\f'\0\0"..., 4096) = 4096 write(9, "a\200\267{e\231\231\231\330c\205\350\305\345\33_GTU\377|\203\225\344\344\344\204B\373A\21\233"..., 4096) = 4096 write(9, "\216f'\336Q\230\3019\317d\323\34\316f\263\234U\304\337Q\230\3019\317d\261K+\336\311\347<"..., 4096) = 4096 write(9, "\24\200\223"..., 4096) = 4096 write(9, "+\v\343\220\365Dh\341\212\257lY\16f\211\1\362\242\364\v(\361\274\370\261G\"\27}\3006\337"..., 4096) = 4096 write(9, "\0234\34C\326\310\256\245\353\254\251\201\205\37\340\265\245Dm \3366b\323\0025\346,?%\376\262"..., 4096) = 4096 write(9, "L\365\312\221/\214\310\273$Pl\35\204T\20\4\377\366\351z\306\334\217n3<\334\34\351\241\207\252"..., 4096) = 4096 write(9, "\276y\v\270\267\204%\253c7\24\0103\\\275s\304\274C\5s0\366\7\327\224\20\3k\310\224\220"..., 4096) = 4096 write(9, "l\35y\24\224\207\301\223o\323\270p\256\24\230\203\354\275\306\1b0\201\202\251\23\35f\333\216\341\7"..., 4096) = 4096 write(9, "\315\22\27\24\362!\235\307\341D\324\204\311\212\307^\"2\2752\310\255AO\337\304\370\262\232H\336e"..., 4096) = 4096 write(9, "\244\2\217\337|P8\212\2517\271N\260\372\330\3639\314~\213\353\0\243\321\22\305g\246\373\34\330\25\377\374iz"..., 4096) = 4096 write(9, "PkQ\364m8\347\0g%\250kjs?\374P\234\25T\270\6\303*\351ur\2\335\3161\37"..., 4096) = 4096 write(9, "\355\362\350\356m\17WG\232$\212\377yDq\265{\23\233i.k\226^\216\f\243\211)QQk"..., 4096) = 4096 write(9, "\301\262\364\0364\214\302er|\5.\270\327\327\320\210\302eb\3543\236r\346g\302Zm\333\227\272"..., 4096) = 4096 write(9, "\221\325\226\346\226\310\243~\251g\342V9l\344\370\247\3217\220)\1\200\t\347\3412\275\255\274\345\242"..., 4096) = 4096 write(9, "\24!\32E,n\342\323\244\5\213\226\16\267\33\275m\347\356\334\267o1A\3.\351erJ\252\357"..., 4096) = 4096 write(9, "\205\220a?6!9[\336lW/\300\0160\5\354\0S\300\16\365\2\366c\1\0\0\0\350%\0"..., 4096) = 4096 write(9, "V\346Y\242*p\356~\237Ga\341CmF\30\231[\f\263\265\213\210\214\244n\363q\2342\315\310"..., 4096) = 4096 write(9, "\333\350\323\333\334,\354^\250\251\211\311\272\215>\3222RQQ\271\34x~\213\317\206\305\313V\304F"..., 4096) = 4096 write(9, "K\206p\v\332\16\217\302\302\355\306\214\355\321\313\214\376\312\312nd\350\275{eee\1\27\2\355\306"..., 4096) = 4096 read(8, "\237\225\0323\353\223\331j\271H~\220\343\355\232\334>\335h&\270\240 v\21\26s)\274\250\270X"..., 4096) = 4096 read(8, "\5\2I\224\264\21\374\355B2\312\243\363\vY1Fqh\30-nL\257\214\250\322\236\5\277\315\252"..., 4096) = 4096 read(8, "G\255\333\6\376\322\333i_\330d\252\22\23\36|\214\326\267\31\321\267\262l\323\214\277\220\272m\211\24"..., 4096) = 4096 read(8, "\222\2403\212\347-\315\201\371tr\277\315\3275pn\356W\24C\351+\210\327h\365\16\21G\321V"..., 4096) = 4096 read(8, "_K\212?\373;\277T\244\325\370\312w\265A)gQ:h\31\265_:\"|T\251\276\343\n!"..., 4096) = 4096 read(8, "\256I\362d\264\352\t\266_\247i0n\222\1&\2253\261p\374,g+2\0\325\7{\322*r"..., 4096) = 4096 read(8, "\252\361\324\2248\336\240Gf%\246\360&\312\266\t\230\377\273\215\216n\2\206\254\251\201\301\322\3104\225"..., 4096) = 4096 read(8, "M\264\26\266\216 \344\21\16~\216\205Q\317\361\313;\316\36\2428\2711\27c\235K\240\213b\337s"..., 4096) = 4096 read(8, "\340\v\277\334R<\312:\231h\347Ru\234\247E\214sl\343\207\307j\253\274\323/\233\255{X\213"..., 4096) = 4096 read(8, "\310\235?\254\306\365\254.~\275\337\224V\2777\300\312\n\250{\330\203#\277\243\35\341\227H\244\215H"..., 4096) = 4096 read(8, "\263\353\311\306\351%\341\275\317\312\337X\255hZP\246\22P\200\343\354\3\241e\266\24\322\376\251\225?"..., 4096) = 4096 read(8, "4\331\313\37\263\265J\233\303!2\242\336D\\&\337\331f\373\213`\264\324\215\304\v6'\353^^"..., 4096) = 4096 read(8, "t\216\276\245P\224c\333@J<\17\313O\26\23\22\366\0W\307\271\24\313n*\345k\254\246\211\216"..., 4096) = 4096 read(8, "\265\205d\267\263\324{\354\362\273\v71\251\342\247\367\376&\215,\275\7\216:\317\276z\362I\362g"..., 4096) = 4096 read(8, "s?{\2731\315?\37\221\224\2220c\215Y\25\362O\240%wl\365\375\262cx\10;\f\3550"..., 4096) = 4096 read(8, "\0314MiT\374z\3127z:C\317\356\200\36 \353\231\254D|\353\210\260\360\240G\330\373d="..., 4096) = 4096 read(8, "\335RR\337\"r\342x\220\221U\375H\315J\213\303k-\2518\244\4\vcZ\207\234\327q\270,"..., 4096) = 4096 read(8, "\223\315x8=?\377\325\374\321>\16\16\316G\v\213\1}\264C\321\354\274<\361\233\344\314\202\2I"..., 4096) = 4096 read(8, "o\275\226\201\201\301\24\243h\24\317\1\17U\220\2\333\373\344\203G\r\350J\3\32\275\335\17\214C?"..., 4096) = 4096 read(8, "\355]X\10\313\3qc\2}K\310\235\35\236\266'W\322\341\334$.\322\332y.~w\33\306\26"..., 4096) = 4096 read(8, "7.pngPK\1\2\24\0\24\0\10\10\10\0\271dhD\306\266\377\31\220.\0\0R2\0"..., 4096) = 1052 read(8, "", 4096) = 0 read(8, "", 4096) = 0 read(6, "\270\3g\2\351\3\352\177\316\333\274\243\1\227\3o\334)*\376(\346\225\346\274\315\353\360\177m\350\203"..., 4096) = 4096 read(6, "a\320M\275\344x(}\24K.\226C/\215p\374\22?\241\20\7\203\35\277\274u\373\266\320{4"..., 4096) = 4096 read(6, "\364Je\345\33\201\301\314\275\344+\313\227\350\27t\34~\316?\27\366V\204\273\227\17\375Q`\330\244"..., 4096) = 4096 read(6, "\26\316\5r\2632<]\34:w\356l}\304QH$\27\tU\27\302:\325U\225\17n\335P\36"..., 4096) = 4096 read(6, "\245\254\305\334\"\341\376\227\317\333\37\22\222?p[\304\304\304\244\345B\374\305x\235\345\313\247\261\237s"..., 4096) = 4096 read(6, "\227\16\222\2\350\350ke-\356]\241\242\243\224DG\251vdG\201 \10\302%\2654\332\327\312:"..., 4096) = 4096 read(6, "~~\376\314\2313e[\376T,\322\3668w\356\\\247N\235\f\r\r\tc\fq\200i\246\350\326"..., 4096) = 4096 read(6, ")Q{\301?\223\3736Kg\240>\257\326\231\372\3441,7\355\365\3537`\20\275\222U\16\367T"..., 4096) = 4096 read(6, "f\326\223\307\260\335\177\324k\304\330\361\344J\272A\356\0iY\220\332\302\274w\f\244\226\33\36\"\210"..., 4096) = 4096 read(6, "\355+7\234G\272\2<\220Z\30\02200\342#\303\26,3\254\251\372\331Wr\300\322\225\246\0205"..., 4096) = 4096 read(6, "\266\251\273/\236=!KR\\\324\322\r\333\7\217\32\313\254\360s\301\247%\366\26d\200g\226\274L"..., 4096) = 4096 read(6, "\23\35f\325u\247\324\t\0\305EE'\217\36\214\16\373\317L}\261\341\301\373\267\271\221\277Y\352."..., 4096) = 4096 read(6, "\230\233\363\232\272\\\302\262i\223\236=\274O=\312<\7\213\363\311X'\374}\375}\351,,,L^5\326\317T\315\2773\363\356\375\260Y\355t\4%\355\360\341\310e\250\337\331\223"..., 4096) = 4096 read(6, "\245\327\t\247\313\27\375M\226\256\"\205\302\325\227\322\316\375\222\243;w\276\276\331b\266JH\16 \253"..., 4096) = 4096 read(6, "\222\226\362\356o\207}\302\302m\26X\256\33=iZ\273v\355\237>\210:w\362HB\354K?\367"..., 4096) = 4096 read(6, "\321\262\231dC\267\2\0@\264\224\224U|\377Q\336\320\255h\202\204\2267\3304\373s\317v\341\257"..., 4096) = 4096 read(6, "`\324\255\372\361 \352_\3\0\324\37\217+\371\230\352\177\372\243\250\335\307/)\361\266\327\276\235\2572"..., 4096) = 4096 read(6, "s\363\332\3A\221\324\10P\2]5s\25\202x\343>\346\276ep\234\260\341\177\304\235:\233\205\271"..., 4096) = 4096 read(6, "K\0h:\250H1c\336R\353\371\313\204\263\306\327Y/\311\343B3\3\256\257\346\345\262\337\377W"..., 4096) = 4096 read(6, "\251\276\300\332\326O\1K{O2\262\16/\331 \235\16!?;\360A.UNe\210$ZB%"..., 4096) = 4096 read(6, "\0\0\0\0\0\0\0\0\0\0\17\263\1\0Pictures/100000000"..., 4096) = 1288 read(6, "", 4096) = 0 read(6, "", 4096) = 0 write(7, "\214\355\321\313\214\376\312\312nd\350\275{eee\1\27\2\355\306\214Ul;\260\233b\357\201\203\273"..., 4096) = 4096 write(7, "\270\3g\2\351\3\352\177\316\333\274\243\1\227\3o\334)*\376(\346\225\346\274\315\353\360\177m\350\203"..., 4096) = 4096 write(7, "a\320M\275\344x(}\24K.\226C/\215p\374\22?\241\20\7\203\35\277\274u\373\266\320{4"..., 4096) = 4096 write(7, "\364Je\345\33\201\301\314\275\344+\313\227\350\27t\34~\316?\27\366V\204\273\227\17\375Q`\330\244"..., 4096) = 4096 write(7, "\26\316\5r\2632<]\34:w\356l}\304QH$\27\tU\27\302:\325U\225\17n\335P\36"..., 4096) = 4096 write(7, "\245\254\305\334\"\341\376\227\317\333\37\22\222?p[\304\304\304\244\345B\374\305x\235\345\313\247\261\237s"..., 4096) = 4096 write(7, "\227\16\222\2\350\350ke-\356]\241\242\243\224DG\251vdG\201 \10\302%\2654\332\327\312:"..., 4096) = 4096 write(7, "~~\376\314\2313e[\376T,\322\3668w\356\\\247N\235\f\r\r\tc\fq\200i\246\350\326"..., 4096) = 4096 write(7, ")Q{\301?\223\3736Kg\240>\257\326\231\372\3441,7\355\365\3537`\20\275\222U\16\367T"..., 4096) = 4096 write(7, "f\326\223\307\260\335\177\324k\304\330\361\344J\272A\356\0iY\220\332\302\274w\f\244\226\33\36\"\210"..., 4096) = 4096 write(7, "\355+7\234G\272\2<\220Z\30\02200\342#\303\26,3\254\251\372\331Wr\300\322\225\246\0205"..., 4096) = 4096 write(7, "\266\251\273/\236=!KR\\\324\322\r\333\7\217\32\313\254\360s\301\247%\366\26d\200g\226\274L"..., 4096) = 4096 write(7, "\23\35f\325u\247\324\t\0\305EE'\217\36\214\16\373\317L}\261\341\301\373\267\271\221\277Y\352."..., 4096) = 4096 write(7, "\230\233\363\232\272\\\302\262i\223\236=\274O=\312<\7\213\363\311X'\374}\375}\351,,,L^5\326\317T\315\2773\363\356\375\260Y\355t\4%\355\360\341\310e\250\337\331\223"..., 4096) = 4096 write(7, "\245\327\t\247\313\27\375M\226\256\"\205\302\325\227\322\316\375\222\243;w\276\276\331b\266JH\16 \253"..., 4096) = 4096 write(7, "\222\226\362\356o\207}\302\302m\26X\256\33=iZ\273v\355\237>\210:w\362HB\354K?\367"..., 4096) = 4096 write(7, "\321\262\231dC\267\2\0@\264\224\224U|\377Q\336\320\255h\202\204\2267\3304\373s\317v\341\257"..., 4096) = 4096 write(7, "`\324\255\372\361 \352_\3\0\324\37\217+\371\230\352\177\372\243\250\335\307/)\361\266\327\276\235\2572"..., 4096) = 4096 write(7, "s\363\332\3A\221\324\10P\2]5s\25\202x\343>\346\276ep\234\260\341\177\304\235:\233\205\271"..., 4096) = 4096 write(7, "K\0h:\250H1c\336R\353\371\313\204\263\306\327Y/\311\343B3\3\256\257\346\345\262\337\377W"..., 4096) = 4096 write(7, "\251\276\300\332\326O\1K{O2\262\16/\331 \235\16!?;\360A.UNe\210$ZB%"..., 4096) = 4096 write(9, "\277+7\17\371\5e\275\310\241\334\206\240\0\205\210\34\205\352\10\r\205\347N\200\205\321\30\0\202:\204"..., 4096) = 4096 write(9, "\35\277\223\21hkk[\266l\331\363\317?oee\365\312+\257\344\347\347\313D\240c\16\352DC"..., 4096) = 4096 write(9, "\205\32U\273\21.f\320u>Z\260p\377\201\203\255\255\255\337n\337\21\277p\261T*\365\237\370f"..., 4096) = 4096 write(9, "d\232\307\320\242\202\32\275\32Y\271\205\220\303H;\0m\0\341: \247v\35>\371\373\32\3\217S"..., 4096) = 4096 write(9, "\v\261%Tc8\215\323\21tw\232\205M\325\205\21\221\371\37\21y\327\256]|n\221mjkk"..., 4096) = 4096 write(9, " \10\202 \10\371\240&C\20\4A\20\4!\37\324d\10\202 \10\202 \344#UZQ\213\223\306"..., 4096) = 4096 write(9, "\230:{\36UM\343BLd\372\213\224\342\242\2\325\336\32\377\2318y\336\202%\304\2114\2\221\366"..., 4096) = 4096 write(9, "\330Bg\27(y9\331\311\367n\335\276r\1\4\250\357\16\327\352\252*\3026\33A\20\4\21$b"..., 4096) = 4096 write(9, "\260\277\224\264\333\331\10KS\375\350\260\240\351\32\363\225&N\341\225\267\10-2\244A d\205\371\271"..., 4096) = 4096 write(9, "\343\270\235\365\311\360X\362\31\3478\34\354N\356\266V\377\273\177g\343\236?\377\363\373?W{g-"..., 4096) = 4096 write(9, "\241j\267\242\312\213\v\v\311\250\274q\351\334\237?\177R\345w\257_&K\372\223G\314\17\220\310P"..., 4096) = 4096 write(9, "\264g\231i\317.\236\213\361<\34\306\374\236%.<\304\327sS\351\217\37\324\335W\31\351d\271\236"..., 4096) = 4096 write(9, "`e|\371\234UYY\321EAIN"..., 4096) = 4096 write(9, "\255g\37\241\252a\325\330\257\t\306*\361M\325\0\302\343\246\316d+\37?u&\211nO\356\336\256"..., 4096) = 4096 write(9, "\267\356!\371i\371\246?\355\266\254\371k\327\226\243~\241\324\230S\274\336:\265\25^t\225\354\254\217"..., 4096) = 4096 write(9, "\214P\365\230\300z\363\26M\236eH\357\317\310X\5\235u\377\226\373u\356\342\25\346\253\326S%d"..., 4096) = 4096 write(9, "\257S\236G\35~\2338EA\271k\2756**Z\365FZT\370=,\340\334\322u[\307O\233"..., 4096) = 4096 write(9, "6\10\227\0\0\0\0@\233f\371Ee\25\r\335\10q#%\301h\323BJ\10+\372R\\\206w"..., 4096) = 4096 write(9, "r\232\355o\177\307\236\177\371\354\211\247\313v\207-{Y_\212\16\t\270s\355\312\3737\331\16[\367"..., 4096) = 4096 write(9, "\324\357\330\360\361\223\250\221eD\220\3603%\245\270\250\320m\363\332\212\362\362%\353\266\222\10\265`\365"..., 4096) = 4096 write(9, "m\247\230['\304\305\204\371\35{\226\374h\357\206\225\307\335\367\370F_\23D\313\2330\341w\3\6"..., 4096) = 4096 write(9, "\262\342\35u\0344\266\3444\276\313\203\0f\7\307\241d\255\203h\37\5\25\257:\253\237\2U\\\322"..., 4096) = 4096 read(6, "", 4096) = 0 close(8) = 0 write(7, "\0\0\0\0\0\0\0\0\0\0\17\263\1\0Pictures/100000000"..., 1288) = 1288 close(7) = 0 close(6) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\240\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741825, len=1}) = 0 lseek(3, 125952, SEEK_SET) = 125952 read(3, "\n\0\0\0\20\0\303\0\1\307\1\373\2.\2b\2\226\1\223\0\367\0\303\2\312\2\375\0031\1+"..., 1024) = 1024 rename("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/tmp/svn-VYI0r2", "/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/pristine/89/89fca0c3d11ef3e57ccef84637945108c9785928.svn-base") = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/pristine/89/89fca0c3d11ef3e57ccef84637945108c9785928.svn-base", {st_mode=S_IFREG|0664, st_size=197896, ...}) = 0 lseek(3, 162816, SEEK_SET) = 162816 read(3, "\r\0\0\0\3\2\342\0\3\241\3B\2\342\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 54272, SEEK_SET) = 54272 read(3, "\2\1\370\0\21\0L\3\0\0\0\215\2S\0\253\0|\1\311\1\231\3p\1j\3\320\0\333\3A"..., 1024) = 1024 lseek(3, 102400, SEEK_SET) = 102400 read(3, "\n\0\0\0\23\0\302\0\0\302\0\356\1\32\1E\1q\1\235\1\311\1\365\2 \2L\2x\2\244"..., 1024) = 1024 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", O_RDWR|O_CREAT|O_CLOEXEC, 0644) = 6 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 geteuid() = 530 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 lseek(6, 0, SEEK_SET) = 0 write(6, "\331\325\5\371 \241c\327\377\377\377\377\0265o\343\0\0\0\241\0\0\2\0\0\0\4\0\0\0\0\0"..., 512) = 512 lseek(6, 512, SEEK_SET) = 512 write(6, "\0\0\0e", 4) = 4 lseek(6, 516, SEEK_SET) = 516 write(6, "\n\0\0\0\23\0\302\0\0\302\0\356\1\32\1E\1q\1\235\1\311\1\365\2 \2L\2x\2\244"..., 1024) = 1024 lseek(6, 1540, SEEK_SET) = 1540 write(6, "\0265p\344", 4) = 4 lseek(6, 1544, SEEK_SET) = 1544 write(6, "\0\0\0|", 4) = 4 lseek(6, 1548, SEEK_SET) = 1548 write(6, "\n\0\0\0\20\0\303\0\1\307\1\373\2.\2b\2\226\1\223\0\367\0\303\2\312\2\375\0031\1+"..., 1024) = 1024 lseek(6, 2572, SEEK_SET) = 2572 write(6, "\0265p\353", 4) = 4 lseek(6, 2576, SEEK_SET) = 2576 write(6, "\0\0\0\240", 4) = 4 lseek(6, 2580, SEEK_SET) = 2580 write(6, "\r\0\0\0\3\2\342\0\3\241\3B\2\342\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 3604, SEEK_SET) = 3604 write(6, "\0265p\27", 4) = 4 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 lseek(6, 3608, SEEK_SET) = 3608 write(6, "\0\0\0\1", 4) = 4 lseek(6, 3612, SEEK_SET) = 3612 write(6, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\240\0\0\0\241"..., 1024) = 1024 lseek(6, 4636, SEEK_SET) = 4636 write(6, "\0265o\343", 4) = 4 lseek(3, 0, SEEK_SET) = 0 write(3, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\241\0\0\0\241"..., 1024) = 1024 lseek(3, 102400, SEEK_SET) = 102400 write(3, "\n\0\0\0\24\0\226\0\0\302\0\356\1\32\1E\1q\1\235\1\311\1\365\2 \2L\0\226\2x"..., 1024) = 1024 lseek(3, 125952, SEEK_SET) = 125952 write(3, "\n\0\0\0\21\0\217\0\0\217\1\307\1\373\2.\2b\2\226\1\223\0\367\0\303\2\312\2\375\0031"..., 1024) = 1024 lseek(3, 162816, SEEK_SET) = 162816 write(3, "\r\0\0\0\4\2\202\0\3\241\3B\2\342\2\202\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 close(6) = 0 unlink("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal") = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=2}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 epoll_ctl(4, EPOLL_CTL_DEL, 5, {0, {u32=0, u64=0}}) = 0 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLOUT, {u32=3303153608, u64=140397194125256}}) = 0 epoll_wait(4, {{EPOLLOUT, {u32=3303153608, u64=140397194125256}}}, 16, 500) = 1 write(9, "\0\0\0\0\0\0005#\2\0Pictures/1000000000000"..., 1011) = 1011 lseek(9, 0, SEEK_SET) = 0 stat("/tmp/svn-4fJ8w7", {st_mode=S_IFREG|0600, st_size=197619, ...}) = 0 mmap(NULL, 197619, PROT_READ, MAP_SHARED, 9, 0) = 0x7fb0c4143000 writev(5, [{"PUT /svn/aprx/!svn/wrk/9ce1e70b-"..., 96}, {"Host", 4}, {": ", 2}, {"repo.ham.fi", 11}, {"\r\n", 2}, {"Authorization", 13}, {": ", 2}, {"Basic b2gybXFrOnJpZnJhZjIy", 26}, {"\r\n", 2}, {"User-Agent", 10}, {": ", 2}, {"SVN/1.8.8 (x86_64-redhat-linux-g"..., 46}, {"\r\n", 2}, {"Content-Type", 12}, {": ", 2}, {"application/vnd.svn-svndiff", 27}, {"\r\n", 2}, {"Accept-Encoding", 15}, {": ", 2}, {"gzip", 4}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 48}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 52}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 55}, ...], 54) = 69504 writev(5, [{"\206\7\3553V\314\234\370\370\336\235\372r\362%\223%\243W\361W\311\237>d\320\355\317}\373WU"..., 128757}, {"\r\n", 2}, {"0\r\n\r\n", 5}], 3) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(4, {{EPOLLOUT, {u32=3303153608, u64=140397194125256}}}, 16, 500) = 1 writev(5, [{"\206\7\3553V\314\234\370\370\336\235\372r\362%\223%\243W\361W\311\237>d\320\355\317}\373WU"..., 128757}, {"\r\n", 2}, {"0\r\n\r\n", 5}], 3) = 128764 epoll_ctl(4, EPOLL_CTL_DEL, 5, {0, {u32=0, u64=0}}) = 0 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN, {u32=3303153656, u64=140397194125304}}) = 0 epoll_wait(4, {}, 16, 500) = 0 epoll_wait(4, {{EPOLLIN, {u32=3303153656, u64=140397194125304}}}, 16, 500) = 1 read(5, "HTTP/1.1 204 No Content\r\nDate: M"..., 8000) = 106 munmap(0x7fb0c4143000, 197619) = 0 close(9) = 0 write(1, ".", 1) = 1 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.pdf", {st_mode=S_IFREG|0664, st_size=438348, ...}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\241\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.pdf", O_RDONLY|O_CLOEXEC) = 6 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/tmp/svn-2LgtUH", O_RDWR|O_CREAT|O_EXCL, 0600) = 7 fcntl(7, F_GETFD) = 0 fcntl(7, F_SETFD, FD_CLOEXEC) = 0 fstat(7, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0 chmod("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/tmp/svn-2LgtUH", 0664) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\241\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\241\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/pristine/80/801e13de8f6f628e0af791392a3cdbe991e46277.svn-base", O_RDONLY|O_CLOEXEC) = 8 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\241\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\241\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 open("/tmp/svn-S2P1pi", O_RDWR|O_CREAT|O_EXCL, 0600) = 9 fcntl(9, F_GETFD) = 0 fcntl(9, F_SETFD, FD_CLOEXEC) = 0 read(8, "%PDF-1.4\n%\303\244\303\274\303\266\303\237\n2 0 obj\n<\203\20\253\311\371\0004\305\320\31"..., 4096) = 4096 read(8, "BB\345\326U\33wX\266o(\376\\9vPf\367^\371o\257+\252>S\361\305W5\327\276"..., 4096) = 4096 read(8, "\273\310\226\224\27\26\331R-#\357/\274<\355xy\266\264l9z\353'\345\250\263\331\271\223\32\232"..., 4096) = 4096 read(8, "\31\335z\312\0263\266i\234\fu\345'g\312\17\325\324\315\245\316\320]\277\253|\330\263\317I\177Y"..., 4096) = 4096 read(8, "\266\37<\361\366\226\242'G\215\r\16\t\221\233RZ\267\251\274|\307\343\201\320\304\32>:\227l\t"..., 4096) = 4096 read(8, "\277\277\243\304E\217\f\331\22\0\0\370+W\37\337rg\325\351Q\23\246FEGKf\213\210\214\32"..., 4096) = 4096 read(8, "O]\3\322\206S\n\215Y\36\2579\23\274\245q\344\243\336r\375\266\342\266Sf\355\233B\312\362\202"..., 4096) = 4096 read(8, "\351ZC\313\217\24\27\36|G\337\fe\330\272\357h\243u\26G\372\177|\217\4Jw\241X\247\274"..., 4096) = 4096 read(8, "\22r(xK\0\0\0\n\201\267\204\340-!\207\202\267\4\0\0\240\20xK\10\336\22r(xK"..., 4096) = 4096 read(8, "\265\214\10$\256\305 U\305\324R\263\4>b\325\24\"6\346\230\222\324\2\373\345\303\327\224!\21\5"..., 4096) = 4096 read(8, "\370\211\354\316\212\212\211\315\330\224\235_Y_v\371n\365\315\207\205g\256c\16\360\336\324Y\255\221\334"..., 4096) = 4096 read(8, "^i\356\331\246\277Z\221\325X\256\244\225\306X\t=\311`D& \fLD`:\2&\256\245\273"..., 4096) = 4096 read(8, "@\17XK\210d\234h\220\214qH\260\26\222\201\310\222\10Q1m\304\223\346\326\222\343^\336\336\21"..., 4096) = 4096 read(8, "p0\224\10\26|\33>*\235,-Ru\343+R2u\301R\207(K#\271(=!\271\217\273"..., 4096) = 4096 read(8, "-o\225/I\324M\364\345PJ\225o\202YJ\"Y\342(y?\0342\340\374\374\265\356x\203'"..., 4096) = 4096 read(8, "\245\342\345;(\306\365\305\263{\256\353q\t\3725\343eZ&J\246\207P\223\205>5\213\246\1~"..., 4096) = 4096 read(8, "\202\202\203\333'\245du\262\233c\200-\204"..., 4096) = 4096 read(8, "\17u?\37\354\210[\213\5K\200[\2\0\0\0\300V\250\352\226G\217\36\225\251\224\2\226\271\345\371"..., 4096) = 4096 read(6, "%PDF-1.4\n%\303\244\303\274\303\266\303\237\n2 0 obj\n<\264\252\37\253\7"..., 4096) = 4096 read(8, "\302g\vrTu9bh0\v9\252\23\362\303\322\vG\314e\264\4D1\212\205N\2\2\221\302"..., 4096) = 4096 read(8, "\227+\323\204[\374\2563\276\t\353\5:X\320\201ut\0F\25U\301\206\211\337\205\365\255\337B)"..., 4096) = 4096 read(6, "\337U\270N\274`\367aa\277|\270\367\17\26/\231\225\10?R\267[\323\262\246b\264A\32]\313"..., 4096) = 4096 read(6, "\242\34*\241\00347cVn>\225/\310}\227\23375#\313\324\306\323v\322\326Ft\375\3070"..., 4096) = 4096 read(6, "\272~QM3\0335}\373\321\22\v6{\317\251\n\2327\274sW\213\267\312p\tV\6n\311\241"..., 4096) = 4096 read(6, "\346F%q\375\7p#S5\265\367\233\370\2425\353\271Y6\356< T;Y\177c\374\244i="..., 4096) = 4096 read(6, "c\34\205\2234\276J\37e\230\376\4\316)\2\370\374\"\257\257o\213\277Z\227\36=\223xl\322\352"..., 4096) = 4096 read(6, "\204\263\262\240\266\230\22\327\234@\1\262\0\202\365\33\267\326\3!l\5\371\n\236\16\200\2248\24\334\263"..., 4096) = 4096 read(6, "\365y\1\201\357^\377a*\f\254C\36PU\3407\241TBGZ\305\322\250A\n\3l\325=\\"..., 4096) = 4096 read(6, "\1T>\220\360\304\4Fh\372\363\250X\336G=*R\327=\253\217\v\307KA\315e\10\334\274O"..., 4096) = 4096 read(6, ":\351\303\257_\377=\250\203J\237\374\352O\346\2608}Z\16\277\374\370\365\227\337\35\376\363\245N\253"..., 4096) = 4096 read(6, "9\346\tA>/y\262w\210\310\321\336J\341\247\257\254\374\240\355 \3\310\7\235\341\233}1\200,"..., 4096) = 4096 read(6, "R\371*\333\217L\377f?M\365\25\246\321\266\362n\270s\321q\342x\317P\353QB\21%\30\266"..., 4096) = 4096 read(6, "ter/FlateDecode>>\nstream\nx\234\255ZM\2173"..., 4096) = 4096 read(6, "\353\t\231\220\376\2\10D\217\337\206\300\16\215\37\f\1\303\361\213\20\330\221\21\221\323\214\357q\22%\303"..., 4096) = 4096 read(6, "\327\364'6\274\326\322\347\276!\212\217=\277u\276\357\231:\257\3452G\323Hb\203\324\24+\241\256"..., 4096) = 4096 read(6, "\36GT\315SA\341\362\320<\215\3356\327r\2\303\300\230\207\f\322\4i\306fu\250\1\n\tz"..., 4096) = 4096 read(6, "+\315\222\2715Z\177\250\2\27\323Ac\243\202!\5\325\347\356\221bcS}*\207\25\337\263\266\323"..., 4096) = 4096 read(6, "Fz\356\202\254s\27\331n&\24\317\331\323\212C\n\215\313\350\25&9\4\20/\310s\302\342v\265"..., 4096) = 4096 read(6, "\260\333\3714MZ\273]x\303\361\10\20\2029\0341\344\216\303-\333\312I\265\33\265%\252\315y\31"..., 4096) = 4096 read(6, "\351\224\255\306\252\234\255\353Y\204~Q5t\277\177d\206\315)hc\0262\301\37\6\262\r\205\367\341"..., 4096) = 4096 read(6, "\243\374&\245\255\342\342d\323\353\225\30\335\327\251Z\311\36\264\5q6\321\352'\212\203\23\256\224\233\344"..., 4096) = 4096 read(6, "\26\315\333?e\21\33\4\340\323\177\"^*\334\227\351?^\221\350\343\nendstream"..., 4096) = 4096 read(6, "Filter/FlateDecode>>\nstream\nx\234\255Y"..., 4096) = 4096 read(6, "\277\276|Z\251\326\312\36\355\206j\346$3)\225\33 \252\2e\">\333\23\231F\223-\235{J"..., 4096) = 4096 read(6, "lter/FlateDecode>>\nstream\nx\234\255X\313\252"..., 4096) = 4096 read(6, "5\nendobj\n\n158 0 obj\n<\225/\310}\227\23375#\313\324\306\323v\322\326Ft\375\3070"..., 4096) = 4096 write(7, "\272~QM3\0335}\373\321\22\v6{\317\251\n\2327\274sW\213\267\312p\tV\6n\311\241"..., 4096) = 4096 write(7, "\346F%q\375\7p#S5\265\367\233\370\2425\353\271Y6\356< T;Y\177c\374\244i="..., 4096) = 4096 write(7, "c\34\205\2234\276J\37e\230\376\4\316)\2\370\374\"\257\257o\213\277Z\227\36=\223xl\322\352"..., 4096) = 4096 write(7, "\204\263\262\240\266\230\22\327\234@\1\262\0\202\365\33\267\326\3!l\5\371\n\236\16\200\2248\24\334\263"..., 4096) = 4096 write(7, "\365y\1\201\357^\377a*\f\254C\36PU\3407\241TBGZ\305\322\250A\n\3l\325=\\"..., 4096) = 4096 write(7, "\1T>\220\360\304\4Fh\372\363\250X\336G=*R\327=\253\217\v\307KA\315e\10\334\274O"..., 4096) = 4096 write(7, ":\351\303\257_\377=\250\203J\237\374\352O\346\2608}Z\16\277\374\370\365\227\337\35\376\363\245N\253"..., 4096) = 4096 write(7, "9\346\tA>/y\262w\210\310\321\336J\341\247\257\254\374\240\355 \3\310\7\235\341\233}1\200,"..., 4096) = 4096 write(7, "R\371*\333\217L\377f?M\365\25\246\321\266\362n\270s\321q\342x\317P\353QB\21%\30\266"..., 4096) = 4096 write(7, "ter/FlateDecode>>\nstream\nx\234\255ZM\2173"..., 4096) = 4096 write(7, "\353\t\231\220\376\2\10D\217\337\206\300\16\215\37\f\1\303\361\213\20\330\221\21\221\323\214\357q\22%\303"..., 4096) = 4096 write(7, "\327\364'6\274\326\322\347\276!\212\217=\277u\276\357\231:\257\3452G\323Hb\203\324\24+\241\256"..., 4096) = 4096 write(7, "\36GT\315SA\341\362\320<\215\3356\327r\2\303\300\230\207\f\322\4i\306fu\250\1\n\tz"..., 4096) = 4096 write(7, "+\315\222\2715Z\177\250\2\27\323Ac\243\202!\5\325\347\356\221bcS}*\207\25\337\263\266\323"..., 4096) = 4096 write(7, "Fz\356\202\254s\27\331n&\24\317\331\323\212C\n\215\313\350\25&9\4\20/\310s\302\342v\265"..., 4096) = 4096 write(7, "\260\333\3714MZ\273]x\303\361\10\20\2029\0341\344\216\303-\333\312I\265\33\265%\252\315y\31"..., 4096) = 4096 write(7, "\351\224\255\306\252\234\255\353Y\204~Q5t\277\177d\206\315)hc\0262\301\37\6\262\r\205\367\341"..., 4096) = 4096 write(7, "\243\374&\245\255\342\342d\323\353\225\30\335\327\251Z\311\36\264\5q6\321\352'\212\203\23\256\224\233\344"..., 4096) = 4096 write(7, "\26\315\333?e\21\33\4\340\323\177\"^*\334\227\351?^\221\350\343\nendstream"..., 4096) = 4096 write(7, "Filter/FlateDecode>>\nstream\nx\234\255Y"..., 4096) = 4096 write(7, "\277\276|Z\251\326\312\36\355\206j\346$3)\225\33 \252\2e\">\333\23\231F\223-\235{J"..., 4096) = 4096 write(7, "lter/FlateDecode>>\nstream\nx\234\255X\313\252"..., 4096) = 4096 write(9, "\336\370\244A\2127B\r\372\0371\277\372\325\257V\256\\\231\233\233\253\331\32\t\362\207\213\327Z\24\274"..., 4096) = 4096 write(9, "%\vB\253\363\324\325`\300Q\3\215;\21m\244\365\203\210\346\226e\3\23\22\201,\347_\333\262C"..., 4096) = 4096 write(9, "&\27\23\273\v\2\335\5\223\36Lg\25\3\301\357\347\35\\Nc\5\207\\\316\17~T9B\v;"..., 4096) = 4096 write(9, "]\333kU\376`\227e\261\313\222\26\261u\2\36\232z\255\241\225\24\254\36\253$\325=N\353j\275"..., 4096) = 4096 write(9, "\35i\37\254\307\264&\214\337\303\2\177\234s\242d\207\305\325^\366'\305\325\314\201\376\266lw\202\241"..., 4096) = 4096 write(9, "\0\202\3525\n\212\10\325-\334X\371\306\35\347L\300UG\234\307\23\231\205gc\250\302\224\v\216\0"..., 4096) = 4096 write(9, "A\356\214\21\215\316\r\32yld%\30\3322\"\23\224\205\22\31%\310!GK\333\306\20Q\205;"..., 4096) = 4096 write(9, "\31\373lQ\365\234\274z\366\206\365b\323y\336\305\234{\244\331A\367\255m\272z\244\365\226v`\207"..., 4096) = 4096 write(9, "\3~\216;\373\217+\0K\334\24\301\362\371\310*a\3008\251Zb\364\227\26\270Hh\311\266\351e"..., 4096) = 4096 write(9, "@\362Z\273i+!\274\215\324\v$\231[s\300\235\230\303\333\222f%\35?\307\226\207l\34h\267"..., 4096) = 4096 write(9, "5\370\325Wo\304\275Y\216\203ra\177\23F\r\377\262}!\212\202\3508\177\rPb\303\2366C"..., 4096) = 4096 write(9, "/\346q3dw\342*?nAA\303\263\363\202d\271\326L|r\36E\271\254\315#\231\250\"5"..., 4096) = 4096 write(9, "a<\246\327T\372\223\352IR?yi\334\273\327%\3330\356_\32w\372/M\337_\2326;\207"..., 4096) = 4096 write(9, "\316\5\205E\7\271b\267\344\25l\365x\254\6m*\246,\236\26\212\320\365\365\254eW\223-4\344"..., 4096) = 4096 write(9, "{\3\227\313\32\276P\301\210\232\33F\32\350\214\252\305Q8\36\267\35\215\225\342\242\25~\267\"\333u"..., 4096) = 4096 write(9, "l\321\275n/\277\375\372\355\317\177x\371\27}\7\377~\373\307\267\373\367o\336\271\327\370\262\256\361\345"..., 4096) = 4096 write(9, "\316\310\207#\315\265\257\273\235\304\375\361\31\2221\354\214s%(\222\373\334[rg\331\25w0;\343"..., 4096) = 4096 write(9, "\324\236\304\0-\343T&\275&\371B\206\203\34>;\201\223{\23WzV\205\262\f\234\315Y\21o"..., 4096) = 4096 write(9, "U3\3525\311R\262\304\5\ntYX\244\354\364\334\330(\233>\232\322\324\230\315\236El\355-M"..., 4096) = 4096 write(9, "m\24<\2671:\305\323\250\"K\232\16\222O0\343\34r3\346H\227\221l\v\207\270\222\205Z\22"..., 4096) = 4096 write(9, "\311A\2250St\232\\\n\25Uc,Q\311U\202\271TJ\214\33\372z\232\n\226$L_Hm"..., 4096) = 4096 read(8, "\256\272$\244g\214.5aa\320/-Z\247F~z\f\7\264\2329\v\237\f\304\270J\214\340\313"..., 4096) = 4096 read(8, "\317\257r\1T\272\332J8\264\3203\230u\241E9\303i`c\341a\200\200\6\235y\200\20\"\216"..., 4096) = 4096 read(8, "\343\220\261B9%\316r\364t\234\310\306z\301\33\317\246\313\302\2426\352\212\3613\217\312\262b\271r"..., 4096) = 4096 read(8, "\26\217(\373\231\360\2707{T\254\200\202\240\21\234\25\353\206{\245\viq\356\226\34\v\34\370\212\34"..., 4096) = 4096 read(8, "\34\343Wz\304\364\323sS\30\307\243\t\372bt\260\300\350c\26\320'C!\351!b\245]\243\246"..., 4096) = 4096 read(8, "\333+B\306+%\177\313\263U(\301)\244h\ng\215\302\r\334|\324m\240\205\3\21E\30\274\231"..., 4096) = 4096 read(8, "\257\335\235\211\26v\222\314\23X\332\4\231,\33K\260\351\237\315\265\234\212k\234\21\216\255\370\177\235\354"..., 4096) = 4096 read(8, "|\361\366\326\362\251s\26\272UODr\352\306\245q\372\275\f\30\34\274\354\346\376\3\30439kJ"..., 4096) = 4096 read(8, "\216\213\362\232_YSS\223\271j\371\306r1\366\310\321\177=\375UUU\205\270c\212A\273L\7"..., 4096) = 4096 read(8, "xS\226F\10!\361\0026\264\356\355-'?\377\\\330\226\3414\273D\244\244l3\362 \247\375\n"..., 4096) = 4096 read(8, "L\267X?\4k\227\321\320.\31\30\30\30\30\30R\6M\273\314\26\365\352\325\313v\25\10!\204\220"..., 4096) = 4096 read(8, "\332`Vu\365\325W\243&\35:tx\374\361\307\341\5w\335u\27\246\264b\251x\215\342\234s\316"..., 4096) = 4096 read(8, "\320\236\276X\241\255u\215\253\344u;\17&'\353_~En\362\307e_\272?{\371\212|z\365"..., 4096) = 4096 read(8, "\v\356\222\336'<\371\336K\353\241\301G\17\35\341\361\342\36\243\303\212\315\257\371\310-\237\34\342\302."..., 4096) = 4096 read(8, "\264\\\276S\371P*\226\305\"\210Sc\361\344\202\6\321E\364\215\270\20\2\6@G\36&|#\332"..., 4096) = 4096 read(8, "\261\376wMg1\301{\325&\265\305\2446uP\334\232\31\237Y\233e\206\377\357\v\35\2427\311\301"..., 4096) = 4096 read(8, "GU)q7=JS\215\30\377\235\v\277\342\2\237\363\271\250o{\360_z\260\313\343\362\224\25\314"..., 4096) = 4096 read(8, "\320q\374N\177\177\354o\313\210\5\6\260\347d\207\203\356(\352\333:\303B=\24\204\201\30\216\374\247"..., 4096) = 4096 read(8, "\225\314$\355M\"[\23\367'\222m\232;5d\207zNM\352I\33\31$L:h5e\303\7"..., 4096) = 4096 read(8, "\16\263<\346yf\213r\37pH\267\2362B\261\311\22\334\245?\254'H\217\365\372\244\270\250\221x"..., 4096) = 4096 read(8, "`H'[W\241D>\221$\322m\225\214\34j\37]\20\344\226\344`\242\304dJJ\2455\25x"..., 4096) = 4096 read(8, "m\323m\250\2334\326\3759e\313\262\\\345\313\335n\225\245\276\375\357\336K\305\226\343\251\371Y\217s"..., 4096) = 4096 read(8, "$\360+\326\360+\337b\345j\177\200u}\371\261g_\317\232\37{\366\261g\327-kY\325\322\261"..., 4096) = 4096 read(8, "\322\6\337\237\206\357\331\360\335V\370\316>\370\366a\370g\33\276\205\340\337\332\7\217?6\240<\276\17"..., 4096) = 4096 read(6, "/\31`}8\272\327\f\340\344s\232\301g\256\332-\1sY6z&\32\245\252$\300UI\234J"..., 4096) = 4096 read(6, ":\0\360%\0\232\306\366\307\341?\233s\2705\nendstream\nendobj"..., 4096) = 4096 read(6, "\24[O\31\177F\335\222\262\361\210\24\2067L\360\210O\3E\256,7\250G\213\224\271L\360\223\372"..., 4096) = 4096 read(6, "\231\213 \215N\213Yfi_\224)N.\272P\253w\315j\"\364<\314\n\r\351\212T^\274U"..., 4096) = 4096 read(6, "\326>\20\25\21\234\21\210\210\263\306\\\302\262\345\257\2127\3130\205\304\271\217\277\216[\2319g\366}"..., 4096) = 4096 read(6, "\21\251\252\252B\3341\305\240]\272\340\330Ek\376'\273\\h\201\31\370b\203\332t\"\"\262\317\17"..., 4096) = 4096 read(6, "\25\2112`\342\257\330\234\375~\231\242(\267Z\2515q+\23\22=?\363\5\375}W\357\221\343\305"..., 4096) = 4096 read(6, "\250`\355R\7\235y\267\316\322\240\210\224]\6\270b\272y\24:x#\303v\31\257\336,p\326\224"..., 4096) = 4096 read(6, "M5Ep,*e\255\324z*\312|\370\221G\314\327\333\325[I\271G\350\237-\27\0o\270\271"..., 4096) = 4096 read(6, "c\335\307\36{\314\242C\343\306\215\277\365\255oa_\314\265\322,?\20\5\314)\370i\236\23\245\265"..., 4096) = 4096 read(6, "$\20)\34\277\5\341\255\34\377\225q+V\375\365\3320\232D\6.\360\232\323\203\262\313&\371\3150"..., 4096) = 4096 read(6, "\17\260i\17\266\273\t\350\7X\215\1x^\334\362\212|\360\331{\377E|\317\205kK}w\213\201"..., 4096) = 4096 read(6, "\213\233~*,\374\307\241^\237\7B<\331!\4;<\177\37~\232q@\32\236\337\177\235\0\201\316"..., 4096) = 4096 read(6, "f\252pD+\233\231\257]\360\207\317u\310Q\373R\314\342\2341\271*\254\242\252\3519\265\270[\30"..., 4096) = 4096 read(6, "6l\2164+\233\227Ade\223\22i\206MM0`\303\306i\10OC\277\r}\376\265J\337\f"..., 4096) = 4096 read(6, "4\211n\225\325\340\255@\325\373\321\0264I\212Q;\372#\32\303?\2079\324pz\374T\250\266\263"..., 4096) = 4096 read(6, "\373\237\34\335y:\231\341tZ5y\232(\265:\371\267r\26\320o\4O\371\232\234\352m\255\313-"..., 4096) = 4096 read(6, "\203\r\32\203&\205\373\204\7Ts\2117 ..R\245q\221\202\236\23B%\5u\2450X|c"..., 4096) = 4096 read(6, "\343\23\235\207\357\272\vU9\32\243\5\255\35\321^Ggct\0\22!\232\230\206\204\340\270`AU"..., 4096) = 4096 read(6, "\327\272k\273\203\301\241x\213]\257c\276\374\331\324\273O\217\327\317~v\327C\357=>^\273\347\241"..., 4096) = 4096 read(6, "\3057=\344\235L\375\3623j\203^\313\202\201Uk\264,W\320\332\331\0371\344\34595\350\374\352"..., 4096) = 4096 read(6, "\340,\327\302\334\203\322qZ\356A\f3|\234\207\263Hx\231\223\2\257\20^K\310\327ip17"..., 4096) = 4096 read(6, "\375\1L,\4\10\246\26\346\347\3+\305\3630O\346\367+\357\225w\363\201\364\v\4Q\312\260p>"..., 4096) = 4096 read(6, "\321\232\307\326{z\2264&3\23\227\206/q\2\332,x\361\336\343\233\6\16\365\26\206?\3300\342"..., 4096) = 4096 read(6, "w\274(Y\23\274Lce\262\225Y\35\214\345\321P\34\223\253\306\335\270\206\343M5\2475,\240\301"..., 4096) = 4096 write(7, "5\nendobj\n\n158 0 obj\n<\20\25\21\234\21\210\210\263\306\\\302\262\345\257\2127\3130\205\304\271\217\277\216[\2319g\366}"..., 4096) = 4096 write(7, "\21\251\252\252B\3341\305\240]\272\340\330Ek\376'\273\\h\201\31\370b\203\332t\"\"\262\317\17"..., 4096) = 4096 write(7, "\25\2112`\342\257\330\234\375~\231\242(\267Z\2515q+\23\22=?\363\5\375}W\357\221\343\305"..., 4096) = 4096 write(7, "\250`\355R\7\235y\267\316\322\240\210\224]\6\270b\272y\24:x#\303v\31\257\336,p\326\224"..., 4096) = 4096 write(7, "M5Ep,*e\255\324z*\312|\370\221G\314\327\333\325[I\271G\350\237-\27\0o\270\271"..., 4096) = 4096 write(7, "c\335\307\36{\314\242C\343\306\215\277\365\255oa_\314\265\322,?\20\5\314)\370i\236\23\245\265"..., 4096) = 4096 write(7, "$\20)\34\277\5\341\255\34\377\225q+V\375\365\3320\232D\6.\360\232\323\203\262\313&\371\3150"..., 4096) = 4096 write(7, "\17\260i\17\266\273\t\350\7X\215\1x^\334\362\212|\360\331{\377E|\317\205kK}w\213\201"..., 4096) = 4096 write(7, "\213\233~*,\374\307\241^\237\7B<\331!\4;<\177\37~\232q@\32\236\337\177\235\0\201\316"..., 4096) = 4096 write(7, "f\252pD+\233\231\257]\360\207\317u\310Q\373R\314\342\2341\271*\254\242\252\3519\265\270[\30"..., 4096) = 4096 write(7, "6l\2164+\233\227Ade\223\22i\206MM0`\303\306i\10OC\277\r}\376\265J\337\f"..., 4096) = 4096 write(7, "4\211n\225\325\340\255@\325\373\321\0264I\212Q;\372#\32\303?\2079\324pz\374T\250\266\263"..., 4096) = 4096 write(7, "\373\237\34\335y:\231\341tZ5y\232(\265:\371\267r\26\320o\4O\371\232\234\352m\255\313-"..., 4096) = 4096 write(7, "\203\r\32\203&\205\373\204\7Ts\2117 ..R\245q\221\202\236\23B%\5u\2450X|c"..., 4096) = 4096 write(7, "\343\23\235\207\357\272\vU9\32\243\5\255\35\321^Ggct\0\22!\232\230\206\204\340\270`AU"..., 4096) = 4096 write(7, "\327\272k\273\203\301\241x\213]\257c\276\374\331\324\273O\217\327\317~v\327C\357=>^\273\347\241"..., 4096) = 4096 write(7, "\3057=\344\235L\375\3623j\203^\313\202\201Uk\264,W\320\332\331\0371\344\34595\350\374\352"..., 4096) = 4096 write(7, "\340,\327\302\334\203\322qZ\356A\f3|\234\207\263Hx\231\223\2\257\20^K\310\327ip17"..., 4096) = 4096 write(7, "\375\1L,\4\10\246\26\346\347\3+\305\3630O\346\367+\357\225w\363\201\364\v\4Q\312\260p>"..., 4096) = 4096 write(7, "\321\232\307\326{z\2264&3\23\227\206/q\2\332,x\361\336\343\233\6\16\365\26\206?\3300\342"..., 4096) = 4096 mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb0c4152000 write(9, "*\343:\347\363\222\313SJx'\27\215\247FY\323\273\214Z\263\342\227\21\235\373\0o\246\377\254\36"..., 4096) = 4096 write(9, "\320\333\302\v=\224\336\272\273\320C\241{\351\337\257\244\371\360\330\361\204\335\22H\34\217Fz$=#"..., 4096) = 4096 write(9, "3e\367\262\357V\323\236X\2301\346hSUL&\v\207\246\340\36\26\203\177\336\3\f\354V\232\226"..., 4096) = 4096 write(9, "6\341\25\225\274\212\212WlM\230\345\31o\213A\305$G\325\362\20\271\367\31}\331\231\233E\366="..., 4096) = 4096 write(9, "\323\201)\320E\341\314d\36\303Le\2\277\3601\214\344\334\361\350\325\326\3640\360\223g\300|fs"..., 4096) = 4096 write(9, "\302\322\307u}A\331\210\303\7\243\331\224`\215\225v\1&r\3370\260\243\304\222\242\255\2056._"..., 4096) = 4096 write(9, "j\n\n209 0 obj\n1357\nendobj\n\n211 0 "..., 4096) = 4096 write(9, "\341%\353\225\270\223\260'\324+\361\223\330+\361J\257$\216\263W\3425}\301q\364J|\276^I"..., 4096) = 4096 write(9, "\303\366\1\334\231\234P\223#P\333<\343\264\225\334k\3109\354\3375\317\210\2B\310\211\234\266sz"..., 4096) = 4096 write(9, "tc\\Ib?e\202\252L\243`0T\3755\23\314\200F\324c\312\6@K\20p_\367R\31"..., 4096) = 4096 write(9, "uTo\1\223Q\231\247j\30X\223F\222\204\337\274\373\275\31\326'`\351\374\206t\265\305Q\210\261"..., 4096) = 4096 write(9, "e[C\363\32qdR\354\23\247&\372\6\6w\364Ml\23\307\206n~\276id\363\340D\337\324"..., 4096) = 4096 write(9, "W\277\267\253w\363Pmf \351\27\2649\256\260\375\252f\274C\256t3HrIM\241F\32b"..., 4096) = 4096 write(9, "\255\3\225\375EE\346\nKQsc\203;\3675\360\256g\324j\r\272\377vw\201;G\5,\224"..., 4096) = 4096 write(9, "N\306\351,\312\236\327\251\213\346\2113C\332T4S\203\372b$}\242\343\365\304\312\361\36^\ta"..., 4096) = 4096 write(9, "\256\233\234(\227\272;[\332\373\232\7:c\33\245\251yi\\Z\230\33\237\230\334;>w\2554\275"..., 4096) = 4096 write(9, "sq\\e\325\357\355\354[\23\262\365X\373p\246\255\261wZ\35!\3372\354\340e\316\316\376el"..., 4096) = 4096 write(9, "\266\356\260\3626+\230\325]v\215GC5\252\200\33\25H\237\336\321h6\253\343\3\"\247V\274&"..., 4096) = 4096 read(8, "\264A\270\204\374\252\312\17_\10{f9T\351W\253\241\210/\r\3664\207\214\327\247~s\23\253\342"..., 4096) = 4096 read(8, "/a\37\210>\360!\277Cps\216M\5*\201\17\361M|\214_\340q\332A\342\37ss\372\21"..., 4096) = 4096 read(8, "\211\213\257_\314J+\307Ka\374\214:\244\201\375K\341|OFuz\240\232\351\270\365\205[V\227"..., 4096) = 4096 read(8, "\3265%[\353\266vne\267\254u\271\23\303K\356\315\303Q\3670vl\1\271\331<\220\0351\17"..., 4096) = 4096 read(8, "#\306\327ew\267\361\242,\27y\\x\t\351\205\264\244x\26\255\205\222\342B\352L\327Su\346,"..., 4096) = 4096 read(8, "D\213\305\266\335v\330F\317\332\300m\v\330\216\332\270\230T\377Y,\26U\375\330\333\27\213\200$\251"..., 4096) = 4096 read(8, "\3660/\177\221\31\7$\327\223\353\313\355\311\35\316\335\221{$We\317\275\234K\323\226\237\326\202G"..., 4096) = 4096 read(8, "xX\317\2627+;M\375X\224\3765J=\21\256\r\3433\210|\357W\270]\250\244\256U\3328"..., 4096) = 4096 read(8, "$\272\300\320b\216\26_\224\361\331_V\\\304\264<\250w\353\323\335zw\r\21ci\370\301\3300"..., 4096) = 4096 read(8, "\302H\203\214\204\3210 l\220\334)\343M2\31\317\341\26(\332I\31\201\5^\325\320\370\221F\303"..., 4096) = 4096 read(8, "SnZ.\323\340\35\361\222}\342\254H\312E\274/\t\317\332\360*}\227\236\34R\343\260zH\275"..., 4096) = 4096 read(8, "00 600 600 600 600 600 600 600 6"..., 4096) = 4096 read(8, "\312\355\315:\222E}\26599\26\347\"U\270\326\350\6\267{A\355}*\230Q\1Q\201J\265\272"..., 4096) = 4096 read(8, "8\17\177\204m\367\220i\371\376^\327N\327\235.n\255\rZ\235\220CA\225`M\240\32'\323\311"..., 4096) = 4096 read(8, "\30\357\33\214h\2356\220A\302v\301\21+\211D\320\347*\v\226\241p\24\321\274\35\243\27\36\231Z"..., 4096) = 4096 read(8, "\247c>\355\n\302\254\213p\rn\3028\33\227`,R\274\231G\310,\3\33\223a"..., 4096) = 4096 read(8, "P\36V\236RN)\\\267\2\271J\231\322\242\20'#\201?)\360\276\2O)?S\360\1\5\6"..., 4096) = 4096 read(8, "\303e\226\215\271\215:9\255\360\312\353U\330\331\275\33\235\333\2569\304\27_\225xv\204\317\242)\4"..., 4096) = 4096 read(8, "\2778\320q\177\320\327\326\256\311\231\315\263\345\34\207#_\256\366\266\310\236&m\256\\\356o\266\311\305"..., 4096) = 4096 read(8, "\203\0258\262\263\202\365\356\360\320wt\5\305\32\f\210\3\2211\252=\235\343\305\22\3\3758Dc5"..., 4096) = 4096 read(8, "\254\252n\3277\314-G^\333\275\357\255\7V\256\30\330\273R\327>\370\322\351\320\334\360\220\266\351\276"..., 4096) = 4096 read(8, "oX\327\323\247\v\375VU1\375\324\255S\301[+V\337\366\340\201\351x\372\331\3576\337\273c\250"..., 4096) = 4096 read(6, "\316\304WX\324\250\340\267\211\300\200\350A\366M@\255\330\354+\301j8F\244]\344\16Gjag"..., 4096) = 4096 read(6, "G\217"..., 4096) = 4096 read(6, "]\374\226\2576s8w\366\224\2\236\17\326\314\316\35\235@\353\262ut\36\227\306MG\331\264\206{"..., 4096) = 4096 read(6, "\"\240$y\221\362\10vE@\214\300\317?\211|\35\301\335\24\300\35\210\274\34y3\302\323\344\351W"..., 4096) = 4096 read(6, "\3467\327\16\236\333\35\232\26v\325\336yl\305m\303\375\223G\217\36\2331\270\2642\330\334\3270\265"..., 4096) = 4096 read(6, "\3469\273\327\314M\33\235\217\233C\233\37{u\305\363\243_\34o\305oh]\24\234\333?\275\244\250"..., 4096) = 4096 read(6, "A\337\3002\277\246)\3124\7\305\275\24S\266k\207p\360\305T\272\300\354\266.\260\347gL\f6"..., 4096) = 4096 read(6, "\216\277\3025\364s\232'\353u\332\206\363\322\347s=~\251\257!\263\252\347C\273\274\356\346\307\363R"..., 4096) = 4096 read(6, "\32\247\344\3620\317\214\37C9,#\273\237G\326\26xa\36\22D\203\214\230eD\220\241\214i\347"..., 4096) = 4096 read(6, "%\222m\352\00147b\"\3768\332v\314Y\205=\7\357\317m*\317E\225`\357\233\375\207\321\343"..., 4096) = 4096 read(6, "\374\340\343\317\36\376\333\373\277\230\234\371\315\7\37\355?\360\321\7\277\336\211c\373p\354\235\322\330+\304"..., 4096) = 4096 read(6, "W\311:\371\36y\355\352x\201\207j(\2\31\371'\330F~L\336#o\220W\310\303\357\233W\20"..., 4096) = 4096 read(6, "z\347\342\227N\356Y\253]\30s%\376J\373o\377\206Z\356C\271\\F;\253$~\362T \272"..., 4096) = 4096 read(6, "\201\35\364\215\355>\312\306\215\233\2731_0\202^X\24\24\5S\320\273u\236Z\363\334\216\20\372\252"..., 4096) = 4096 read(6, "^\22\341q\225F\303s\327\370\342\272A\347V\244\327W\27\210Bny\205e\16c(\201\tF\v"..., 4096) = 4096 read(6, "\364\334:\26]\300\203ES\233\211\236t\210*\213(\2524\372d}\216\276\217-#\361{D\10\210"..., 4096) = 4096 read(6, "\0\275!\351y?|I\251\364\200\37v\372a\243\37j\375P\344\207\17\375\360\242\37\36S*\210\345"..., 4096) = 4096 read(6, "+\271\323\10\235\306\1#5\32u#\211b\354\275\205!\26\337\34R\2\234CW\313g\277\315\21_"..., 4096) = 4096 read(6, "48>>\nstream\nx\234\325\274\7x\33\307\2650:3\273\213^\26\215\0\t\202"..., 4096) = 4096 read(6, "G\36\31\34<>Z\261~\223\252\376\326U\345\313\217\276\17\263\337\7\374\264R\344c\237\220$\223Q"..., 4096) = 4096 read(6, "*\225\354\10\r\335p2\213\270G)\306e6\323\355t\300\230\207\356\276\350\304\220\r/n\31P<"..., 4096) = 4096 read(6, "\244\362\254\330\323\25\330\262<\277a\342\276\245\366\232\206\305\331\331~\343=I+\n<\3\231I\371&"..., 4096) = 4096 read(6, "M>{\337;c\357\354z\207\351{k\354\255]o1\347\277\205\277\31N\267\217\237\303\216s\371\347"..., 4096) = 4096 read(6, "0 0 595 842]/Annots[\n304 0 R 324"..., 4096) = 4096 read(6, "0 R/MediaBox[0 0 595 842]/Group<"..., 4096) = 4096 write(7, "w\274(Y\23\274Lce\262\225Y\35\214\345\321P\34\223\253\306\335\270\206\343M5\2475,\240\301"..., 4096) = 4096 write(7, "\316\304WX\324\250\340\267\211\300\200\350A\366M@\255\330\354+\301j8F\244]\344\16Gjag"..., 4096) = 4096 write(7, "G\217"..., 4096) = 4096 write(7, "]\374\226\2576s8w\366\224\2\236\17\326\314\316\35\235@\353\262ut\36\227\306MG\331\264\206{"..., 4096) = 4096 write(7, "\"\240$y\221\362\10vE@\214\300\317?\211|\35\301\335\24\300\35\210\274\34y3\302\323\344\351W"..., 4096) = 4096 write(7, "\3467\327\16\236\333\35\232\26v\325\336yl\305m\303\375\223G\217\36\2331\270\2642\330\334\3270\265"..., 4096) = 4096 write(7, "\3469\273\327\314M\33\235\217\233C\233\37{u\305\363\243_\34o\305oh]\24\234\333?\275\244\250"..., 4096) = 4096 write(7, "A\337\3002\277\246)\3124\7\305\275\24S\266k\207p\360\305T\272\300\354\266.\260\347gL\f6"..., 4096) = 4096 write(7, "\216\277\3025\364s\232'\353u\332\206\363\322\347s=~\251\257!\263\252\347C\273\274\356\346\307\363R"..., 4096) = 4096 write(7, "\32\247\344\3620\317\214\37C9,#\273\237G\326\26xa\36\22D\203\214\230eD\220\241\214i\347"..., 4096) = 4096 write(7, "%\222m\352\00147b\"\3768\332v\314Y\205=\7\357\317m*\317E\225`\357\233\375\207\321\343"..., 4096) = 4096 write(7, "\374\340\343\317\36\376\333\373\277\230\234\371\315\7\37\355?\360\321\7\277\336\211c\373p\354\235\322\330+\304"..., 4096) = 4096 write(7, "W\311:\371\36y\355\352x\201\207j(\2\31\371'\330F~L\336#o\220W\310\303\357\233W\20"..., 4096) = 4096 write(7, "z\347\342\227N\356Y\253]\30s%\376J\373o\377\206Z\356C\271\\F;\253$~\362T \272"..., 4096) = 4096 write(7, "\201\35\364\215\355>\312\306\215\233\2731_0\202^X\24\24\5S\320\273u\236Z\363\334\216\20\372\252"..., 4096) = 4096 write(7, "^\22\341q\225F\303s\327\370\342\272A\347V\244\327W\27\210Bny\205e\16c(\201\tF\v"..., 4096) = 4096 write(7, "\364\334:\26]\300\203ES\233\211\236t\210*\213(\2524\372d}\216\276\217-#\361{D\10\210"..., 4096) = 4096 write(7, "\0\275!\351y?|I\251\364\200\37v\372a\243\37j\375P\344\207\17\375\360\242\37\36S*\210\345"..., 4096) = 4096 write(7, "+\271\323\10\235\306\1#5\32u#\211b\354\275\205!\26\337\34R\2\234CW\313g\277\315\21_"..., 4096) = 4096 write(7, "48>>\nstream\nx\234\325\274\7x\33\307\2650:3\273\213^\26\215\0\t\202"..., 4096) = 4096 write(7, "G\36\31\34<>Z\261~\223\252\376\326U\345\313\217\276\17\263\337\7\374\264R\344c\237\220$\223Q"..., 4096) = 4096 write(7, "*\225\354\10\r\335p2\213\270G)\306e6\323\355t\300\230\207\356\276\350\304\220\r/n\31P<"..., 4096) = 4096 write(7, "\244\362\254\330\323\25\330\262<\277a\342\276\245\366\232\206\305\331\331~\343=I+\n<\3\231I\371&"..., 4096) = 4096 write(7, "M>{\337;c\357\354z\207\351{k\354\255]o1\347\277\205\277\31N\267\217\237\303\216s\371\347"..., 4096) = 4096 write(7, "0 0 595 842]/Annots[\n304 0 R 324"..., 4096) = 4096 write(9, "M\347B\24\0278\264\10\31[\200\360<\25jU\240\342\2\24\354\24\f\24(Q\230\302\350\232f\214"..., 4096) = 4096 write(9, "4<\355Q\253\305\353\17KG6\36\345i\327|\221G\361\250E\177\2744\371\r\305\242Y\203\367\212"..., 4096) = 4096 write(9, "W\254:\314\264\244\35t\200\356\340f 7\252Rse\273b\307v\2737\301]w\206\351O2R"..., 4096) = 4096 write(9, "C\v\257\304\306\224t\n\230\374\377\4\30:\222\256\340C\337U\"\202(\32*-_0\311\367\302U"..., 4096) = 4096 write(9, "\226x\353\274K\275\33\275\333\275OzOz_\367~\344\375\302\233\360\6\273b/\33\261c\357\177\34"..., 4096) = 4096 write(9, "\3704\315U\370\230f\214?\20\341i\21\366\211PNiq\201\10`\24a\377\n\261_\334%>+"..., 4096) = 4096 write(9, "\2161\313\243\333\f\373\242\331D:e\334\367\257\227\6'\255\373.\221A\6r0J!g\310\30\337"..., 4096) = 4096 write(9, "\32@\364\316\315\5\\\216\300\334\300\334\340\374\342\336\215.\207\3015wZ\243\231\233\252G\f\223\326n"..., 4096) = 4096 write(9, "\246I\37\303?s~\373J\231\17\274\337\300D)\363\255$\211\n78\313BD\351\24E\204\203\37"..., 4096) = 4096 write(9, "\27\4X\2\361\232\375p\3l\350\336;1\320\256'\240>\374\203\205;\245\27\267!\2\331\302\203\330"..., 4096) = 4096 write(9, "A\367l \323\216\244\326\210[\324\272@S \321~\245q\261\221\2666Bc@m\367\7[\2024"..., 4096) = 4096 write(9, "/aP\22\355\25]|\22\265\206\26\10v\27\325\351\202\263\357\275'm\22H\347\306/d\7\321\336"..., 4096) = 4096 write(9, "\312\231,\254%G\345?\352H,\335\272\26207\237\344+f*y\257\333]\317\17\305c|w4"..., 4096) = 4096 write(9, "\225\345\343\312g\200\345\207\315\36s\246\307\354YD\335S\31\360\340\324fa\345\337\236X\304\277\311\214"..., 4096) = 4096 write(9, "\323`\214\315\27\223\331b2\233M\354`\203d\252h\364\2332\235\6\31\302\5f\247H4\215\351&"..., 4096) = 4096 write(9, "\6\266\205\332\2501.3\21\213\211\271B\315\234\206\253\247\200.\7H\32\7\316B7\372v\32\r5"..., 4096) = 4096 write(9, "\375\223m--\326@1k\353\32l\353o>w\2\6\240\377sS\35\3127Q\246\337B{_\305"..., 4096) = 4096 write(9, "\32]\326\266\354g\313\376\260\354?\227\t\17.\3\373\322\214\274Z\373\322T\367\377\323\262\331\3534\f"..., 4096) = 4096 write(9, "jS}\276\224\313*.3f,*\261\367\17z\33\v\223\342\266\202\330\357\231g\250\245\200Q\35\350"..., 4096) = 4096 write(9, "\267\271\300\261/\376x\354\30\253\216\307\264\243\235l\n\333\204\n\300:?&\324\354\310\237\315'\303~"..., 4096) = 4096 write(9, "\331tj\223\4\316R\236\307\22\205J\262\356M|\207\241\250\322\237\22l^\22L\361W\372\f\321\333"..., 4096) = 4096 write(9, "\271\320^\0003\242\20\313C\276\277y\347?\36\365\377\f\0221\230\257\25G\231\22\307\216\365\34\21\307"..., 4096) = 4096 write(9, "aBox[0 0 595 842]/Group<>\ne"..., 4096) = 4096 read(8, "ype/OBJR/Obj 3665 0 R>>\nendobj\n\n"..., 4096) = 4096 read(8, "endobj\n\n4035 0 obj\n<>\nendobj\n\n398 0 obj"..., 4096) = 4096 read(8, "93 0 R\n/A 4098 0 R\n>>\nendobj\n\n40"..., 4096) = 4096 read(8, " R\n/A 4121 0 R\n/K[5 ]\n>>\nendobj\n"..., 4096) = 4096 read(8, "\n>>\nendobj\n\n4137 0 obj\n<>\ne"..., 4096) = 4096 read(6, "293.1 538.8 306.9]/Dest[97 0 R/X"..., 4096) = 4096 read(6, "<>\ne"..., 4096) = 4096 write(7, "293.1 538.8 306.9]/Dest[97 0 R/X"..., 4096) = 4096 write(7, "<>\nendo"..., 4096) = 4096 write(9, " n \n0000210521 00000 n \n00002124"..., 4096) = 4096 read(8, "an\n/P 674 0 R\n/Pg 611 0 R\n/Lang("..., 4096) = 4096 read(8, "t/Block\n/SpaceBefore 11.3\n/Start"..., 4096) = 4096 read(8, "/Span\n/P 735 0 R\n/Pg 683 0 R\n/La"..., 4096) = 4096 read(8, "\n>>\nendobj\n\n770 0 obj\n<>\nendobj\n\n800 0 obj\n<>\nendobj\n\n869 0 obj\n<"..., 4096) = 4096 read(8, "m\n/P 4 0 R\n/Pg 889 0 R\n/A 4358 0"..., 4096) = 4096 read(8, "batim\n/P 4 0 R\n/Pg 889 0 R\n/A 43"..., 4096) = 4096 read(8, "72 0 R ]\n>>\nendobj\n\n970 0 obj\n<<"..., 4096) = 4096 read(8, "obj\n\n1004 0 obj\n<>\nendob"..., 4096) = 4096 read(8, "ype/StructElem\n/S/Span\n/P 1097 0"..., 4096) = 4096 read(8, "\n/Placement/Block\n/TextAlign/Jus"..., 4096) = 4096 read(8, "7 0 R\n>>\nendobj\n\n4478 0 obj\n<>\nendobj\n\n"..., 4096) = 4096 read(8, "bj\n\n4521 0 obj\n<>\nendobj\n\n4570 0 obj\n<>\nendobj\n\n1444 0 obj\n<>\n"..., 4096) = 4096 read(8, "tructElem\n/S/Span\n/P 1577 0 R\n/P"..., 4096) = 4096 read(8, "\n1607 0 obj\n<>\ne"..., 4096) = 4096 read(8, "673 0 obj\n<>\nendobj\n\n1769 0 obj\n<>\nendobj\n\n4772 0 obj\n"..., 4096) = 4096 read(8, "tElem\n/S/LBody\n/P 1829 0 R\n/Pg 1"..., 4096) = 4096 read(8, "0 R\n/K[1860 0 R ]\n>>\nendobj\n\n186"..., 4096) = 4096 read(8, "R\n/Lang(zxx)\n/K[28 ]\n>>\nendobj\n\n"..., 4096) = 4096 read(8, "lem\n/S/Span\n/P 1931 0 R\n/Pg 1846"..., 4096) = 4096 read(8, "stify\n>>\nendobj\n\n1964 0 obj\n<>\nendobj\n\n2020 0 obj\n<>\nendobj\n\n2145 0 "..., 4096) = 4096 read(8, "ent/Block\n/SpaceBefore 11.3\n>>\ne"..., 4096) = 4096 read(8, " 0 R\n/A 4972 0 R\n/K[29 2207 0 R "..., 4096) = 4096 read(8, "Elem\n/S/verbatim\n/P 4 0 R\n/Pg 22"..., 4096) = 4096 read(8, "\n\n2274 0 obj\n<>\nendobj\n\n2377 0 "..., 4096) = 4096 read(8, "/A 5060 0 R\n/K[37 ]\n>>\nendobj\n\n2"..., 4096) = 4096 read(8, "endobj\n\n2442 0 obj\n<>\nendobj\n\n2468 0 obj\n<>\nendobj\n\n2504 0 obj\n"..., 4096) = 4096 read(8, "gn/Justify\n>>\nendobj\n\n2527 0 obj"..., 4096) = 4096 read(8, "ify\n>>\nendobj\n\n2553 0 obj\n<>\n"..., 4096) = 4096 read(8, "obj\n\n2648 0 obj\n<>\nendobj\n\n5233 0 obj\n<>\nendobj\n\n2741 0 obj\n<>\nendobj\n\n5298 0 "..., 4096) = 4096 read(8, " 2830 0 R\n/A 5313 0 R\n/K[2865 0 "..., 4096) = 4096 read(8, "g 2830 0 R\n/A 5325 0 R\n/K[24 ]\n>"..., 4096) = 4096 read(8, "\n/Pg 2897 0 R\n/Lang(zxx)\n/K[16 ]"..., 4096) = 4096 read(8, "\nendobj\n\n2965 0 obj\n<>\nendobj\n\n3268 0 obj\n<>\nendobj\n\n3332 0 obj\n<<"..., 4096) = 4096 read(8, "t/Block\n/SpaceBefore 11.3\n>>\nend"..., 4096) = 4096 read(8, "/Text#20body\n/P 4 0 R\n/Pg 3388 0"..., 4096) = 4096 read(8, "33 0 obj\n<\0\0>\0i\0\225\0\301\0\355\1\31\1D\1o\1\233\1\307\1\363\2\37"..., 1024) = 1024 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", O_RDWR|O_CREAT|O_CLOEXEC, 0644) = 6 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 geteuid() = 530 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 lseek(6, 0, SEEK_SET) = 0 write(6, "\331\325\5\371 \241c\327\377\377\377\377\267[\205\232\0\0\0\241\0\0\2\0\0\0\4\0\0\0\0\0"..., 512) = 512 lseek(6, 512, SEEK_SET) = 512 write(6, "\0\0\0006", 4) = 4 lseek(6, 516, SEEK_SET) = 516 write(6, "\2\1\370\0\21\0L\3\0\0\0\215\2S\0\253\0|\1\311\1\231\3p\1j\3\320\0\333\3A"..., 1024) = 1024 lseek(6, 1540, SEEK_SET) = 1540 write(6, "\267[\206g", 4) = 4 lseek(3, 115712, SEEK_SET) = 115712 read(3, "\n\0\0\0\25\0l\0\0l\0\230\0\303\0\357\1\33\1G\1s\1\237\1\312\1\366\2!\2M"..., 1024) = 1024 lseek(3, 113664, SEEK_SET) = 113664 read(3, "\n\0\0\0\26\0D\0\0D\0p\0\234\0\310\0\363\1\37\1J\1v\1\241\1\314\1\370\2$"..., 1024) = 1024 lseek(6, 1544, SEEK_SET) = 1544 write(6, "\0\0\0p", 4) = 4 lseek(6, 1548, SEEK_SET) = 1548 write(6, "\n\0\0\0\26\0D\0\0D\0p\0\234\0\310\0\363\1\37\1J\1v\1\241\1\314\1\370\2$"..., 1024) = 1024 lseek(6, 2572, SEEK_SET) = 2572 write(6, "\267[\206\315", 4) = 4 lseek(6, 2576, SEEK_SET) = 2576 write(6, "\0\0\0q", 4) = 4 lseek(6, 2580, SEEK_SET) = 2580 write(6, "\n\0\0\0\26\0>\0\0>\0i\0\225\0\301\0\355\1\31\1D\1o\1\233\1\307\1\363\2\37"..., 1024) = 1024 lseek(6, 3604, SEEK_SET) = 3604 write(6, "\267[\206\250", 4) = 4 lseek(6, 3608, SEEK_SET) = 3608 write(6, "\0\0\0r", 4) = 4 lseek(6, 3612, SEEK_SET) = 3612 write(6, "\n\0\0\0\25\0l\0\0l\0\230\0\303\0\357\1\33\1G\1s\1\237\1\312\1\366\2!\2M"..., 1024) = 1024 lseek(6, 4636, SEEK_SET) = 4636 write(6, "\267[\206\203", 4) = 4 lseek(6, 4640, SEEK_SET) = 4640 write(6, "\0\0\0\213", 4) = 4 lseek(6, 4644, SEEK_SET) = 4644 write(6, "\2\0\0\0\n\1\322\0\0\0\0b\2y\2A\2\t\2\260\2\350\3X\1\322\3 \3\220\3\310"..., 1024) = 1024 lseek(6, 5668, SEEK_SET) = 5668 write(6, "\267[\206\2", 4) = 4 lseek(3, 136192, SEEK_SET) = 136192 read(3, "\n\0\0\0\f\1\222\0\2.\2b\1\372\2\226\2\311\1\222\1\306\2\375\0031\3e\3\231\3\315"..., 1024) = 1024 lseek(3, 160768, SEEK_SET) = 160768 read(3, "\n\0\0\0\n\1\374\0\0020\2c\2\226\2\312\2\375\1\374\0030\3d\3\230\3\314\0\0\0\0"..., 1024) = 1024 lseek(6, 5672, SEEK_SET) = 5672 write(6, "\0\0\0\236", 4) = 4 lseek(6, 5676, SEEK_SET) = 5676 write(6, "\n\0\0\0\n\1\374\0\0020\2c\2\226\2\312\2\375\1\374\0030\3d\3\230\3\314\0\0\0\0"..., 1024) = 1024 lseek(6, 6700, SEEK_SET) = 6700 write(6, "\267[\206\1", 4) = 4 lseek(6, 6704, SEEK_SET) = 6704 write(6, "\0\0\0f", 4) = 4 lseek(6, 6708, SEEK_SET) = 6708 write(6, "\n\0\0\0\22\0]\0\0\371\1-\1a\1\224\1\310\1\373\2/\0]\2c\2\227\2\313\2\377"..., 1024) = 1024 lseek(6, 7732, SEEK_SET) = 7732 write(6, "\267[\207\5", 4) = 4 lseek(6, 7736, SEEK_SET) = 7736 write(6, "\0\0\0\206", 4) = 4 lseek(6, 7740, SEEK_SET) = 7740 write(6, "\n\0\0\0\f\1\222\0\2.\2b\1\372\2\226\2\311\1\222\1\306\2\375\0031\3e\3\231\3\315"..., 1024) = 1024 lseek(6, 8764, SEEK_SET) = 8764 write(6, "\267[\206\230", 4) = 4 lseek(6, 8768, SEEK_SET) = 8768 write(6, "\0\0\0\240", 4) = 4 lseek(6, 8772, SEEK_SET) = 8772 write(6, "\r\0\0\0\4\2\202\0\3\241\3B\2\342\2\202\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 9796, SEEK_SET) = 9796 write(6, "\267[\205\316", 4) = 4 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 lseek(6, 9800, SEEK_SET) = 9800 write(6, "\0\0\0\1", 4) = 4 lseek(6, 9804, SEEK_SET) = 9804 write(6, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\241\0\0\0\241"..., 1024) = 1024 lseek(6, 10828, SEEK_SET) = 10828 write(6, "\267[\205\232", 4) = 4 lseek(3, 0, SEEK_SET) = 0 write(3, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\242\0\0\0\241"..., 1024) = 1024 lseek(3, 54272, SEEK_SET) = 54272 write(3, "\2\1\370\0\21\0L\3\0\0\0\215\2S\0\253\0|\1\311\1\231\3p\1j\3\320\0\333\3A"..., 1024) = 1024 lseek(3, 103424, SEEK_SET) = 103424 write(3, "\n\0\0\0\20\0\306\0\0\306\0\371\1,\1`\1\223\1\306\1\372\2.\2b\2\226\2\312\2\376"..., 1024) = 1024 lseek(3, 113664, SEEK_SET) = 113664 write(3, "\n\0\0\0\26\0D\0\0D\0p\0\234\0\310\0\363\1\37\1J\1v\1\241\1\314\1\370\2$"..., 1024) = 1024 lseek(3, 114688, SEEK_SET) = 114688 write(3, "\n\0\0\0\26\0>\0\0>\0i\0\225\0\301\0\355\1\31\1D\1o\1\233\1\307\1\363\2\37"..., 1024) = 1024 lseek(3, 115712, SEEK_SET) = 115712 write(3, "\n\0\0\0\26\0@\0\0@\0l\0\230\0\303\0\357\1\33\1G\1s\1\237\1\312\1\366\2!"..., 1024) = 1024 lseek(3, 136192, SEEK_SET) = 136192 write(3, "\n\0\0\0\16\1+\0\1+\1_\1\223\1\307\1\373\2/\2b\2\225\2\311\2\374\0030\3d"..., 1024) = 1024 lseek(3, 141312, SEEK_SET) = 141312 write(3, "\2\0\0\0\n\1\322\0\0\0\0b\2y\2A\2\t\2\260\2\350\3X\1\322\3 \3\220\3\310"..., 1024) = 1024 lseek(3, 160768, SEEK_SET) = 160768 write(3, "\n\0\0\0\v\1\306\0\1\306\1\372\2.\2a\2\225\2\311\2\375\0031\3e\3\231\3\315\0\0"..., 1024) = 1024 lseek(3, 162816, SEEK_SET) = 162816 write(3, "\r\0\0\0\5\2\"\0\3\241\3B\2\342\2\202\2\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 close(6) = 0 unlink("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal") = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=2}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 epoll_ctl(4, EPOLL_CTL_DEL, 5, {0, {u32=0, u64=0}}) = 0 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLOUT, {u32=3303153608, u64=140397194125256}}) = 0 epoll_wait(4, {{EPOLLOUT, {u32=3303153608, u64=140397194125256}}}, 16, 500) = 1 write(9, "0000 n \n0000430066 00000 n \n0000"..., 246) = 246 lseek(9, 0, SEEK_SET) = 0 stat("/tmp/svn-S2P1pi", {st_mode=S_IFREG|0600, st_size=315638, ...}) = 0 mmap(NULL, 315638, PROT_READ, MAP_SHARED, 9, 0) = 0x7fb0c4104000 writev(5, [{"PUT /svn/aprx/!svn/wrk/9ce1e70b-"..., 96}, {"Host", 4}, {": ", 2}, {"repo.ham.fi", 11}, {"\r\n", 2}, {"Authorization", 13}, {": ", 2}, {"Basic b2gybXFrOnJpZnJhZjIy", 26}, {"\r\n", 2}, {"User-Agent", 10}, {": ", 2}, {"SVN/1.8.8 (x86_64-redhat-linux-g"..., 46}, {"\r\n", 2}, {"Content-Type", 12}, {": ", 2}, {"application/vnd.svn-svndiff", 27}, {"\r\n", 2}, {"Accept-Encoding", 15}, {": ", 2}, {"gzip", 4}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 48}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 52}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 55}, ...], 54) = 221544 writev(5, [{"\242\5\36\341\313\222\"\237\0053\204\364\f\243\265\0\271\22\2304D\376dVK\206\261\n\370\275+v"..., 94736}, {"\r\n", 2}, {"0\r\n\r\n", 5}], 3) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(4, {{EPOLLOUT, {u32=3303153608, u64=140397194125256}}}, 16, 500) = 1 writev(5, [{"\242\5\36\341\313\222\"\237\0053\204\364\f\243\265\0\271\22\2304D\376dVK\206\261\n\370\275+v"..., 94736}, {"\r\n", 2}, {"0\r\n\r\n", 5}], 3) = 86880 writev(5, [{"00000000 65535 f \n0000399797 000"..., 7856}, {"\r\n", 2}, {"0\r\n\r\n", 5}], 3) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(4, {{EPOLLOUT, {u32=3303153608, u64=140397194125256}}}, 16, 500) = 1 writev(5, [{"00000000 65535 f \n0000399797 000"..., 7856}, {"\r\n", 2}, {"0\r\n\r\n", 5}], 3) = 7863 epoll_ctl(4, EPOLL_CTL_DEL, 5, {0, {u32=0, u64=0}}) = 0 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN, {u32=3303153656, u64=140397194125304}}) = 0 epoll_wait(4, {{EPOLLIN, {u32=3303153656, u64=140397194125304}}}, 16, 500) = 1 read(5, "HTTP/1.1 204 No Content\r\nDate: M"..., 8000) = 106 munmap(0x7fb0c4104000, 315638) = 0 close(9) = 0 epoll_ctl(4, EPOLL_CTL_DEL, 5, {0, {u32=0, u64=0}}) = 0 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLOUT, {u32=3303153608, u64=140397194125256}}) = 0 epoll_wait(4, {{EPOLLOUT, {u32=3303153608, u64=140397194125256}}}, 16, 500) = 1 writev(5, [{"MERGE /svn/aprx/trunk/doc HTTP/1"..., 36}, {"Host", 4}, {": ", 2}, {"repo.ham.fi", 11}, {"\r\n", 2}, {"Authorization", 13}, {": ", 2}, {"Basic b2gybXFrOnJpZnJhZjIy", 26}, {"\r\n", 2}, {"User-Agent", 10}, {": ", 2}, {"SVN/1.8.8 (x86_64-redhat-linux-g"..., 46}, {"\r\n", 2}, {"Accept-Encoding", 15}, {": ", 2}, {"gzip", 4}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 48}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 52}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 55}, {"\r\n", 2}, {"X-SVN-Options", 13}, {": ", 2}, {"release-locks", 13}, ...], 104) = 804 writev(5, [{"2b\r\n", 4}, {"", 1}, {"", 1}, {"", 1}, {"\r\n", 2}, {"0\r\n\r\n", 5}], 12) = 54 epoll_ctl(4, EPOLL_CTL_DEL, 5, {0, {u32=0, u64=0}}) = 0 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN, {u32=3303153656, u64=140397194125304}}) = 0 epoll_wait(4, {{EPOLLIN, {u32=3303153656, u64=140397194125304}}}, 16, 500) = 1 read(5, "HTTP/1.1 200 OK\r\nDate: Mon, 24 M"..., 8000) = 542 brk(0) = 0x7fb0c4e6c000 brk(0x7fb0c4e8d000) = 0x7fb0c4e8d000 brk(0) = 0x7fb0c4e8d000 brk(0) = 0x7fb0c4e8d000 brk(0x7fb0c4e85000) = 0x7fb0c4e85000 brk(0) = 0x7fb0c4e85000 write(1, "\nCommitted revision 590.\n", 25) = 25 epoll_ctl(4, EPOLL_CTL_DEL, 5, {0, {u32=0, u64=0}}) = 0 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLOUT, {u32=3303153608, u64=140397194125256}}) = 0 epoll_wait(4, {{EPOLLOUT, {u32=3303153608, u64=140397194125256}}}, 16, 500) = 1 writev(5, [{"DELETE /svn/aprx/!svn/act/9ce1e7"..., 73}, {"Host", 4}, {": ", 2}, {"repo.ham.fi", 11}, {"\r\n", 2}, {"Authorization", 13}, {": ", 2}, {"Basic b2gybXFrOnJpZnJhZjIy", 26}, {"\r\n", 2}, {"User-Agent", 10}, {": ", 2}, {"SVN/1.8.8 (x86_64-redhat-linux-g"..., 46}, {"\r\n", 2}, {"Accept-Encoding", 15}, {": ", 2}, {"gzip", 4}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 48}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 52}, {"\r\n", 2}, {"DAV", 3}, {": ", 2}, {"http://subversion.tigris.org/xml"..., 55}, {"\r\n", 2}, {"\r\n", 2}], 30) = 396 epoll_ctl(4, EPOLL_CTL_DEL, 5, {0, {u32=0, u64=0}}) = 0 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN, {u32=3303153656, u64=140397194125304}}) = 0 epoll_wait(4, {{EPOLLIN, {u32=3303153656, u64=140397194125304}}}, 16, 500) = 1 read(5, "HTTP/1.1 204 No Content\r\nDate: M"..., 8000) = 106 unlink("/tmp/svn-S2P1pi") = 0 unlink("/tmp/svn-4fJ8w7") = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.odt", {st_mode=S_IFREG|0664, st_size=197896, ...}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\242\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\242\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741825, len=1}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", O_RDWR|O_CREAT|O_CLOEXEC, 0644) = 6 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 geteuid() = 530 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 lseek(6, 0, SEEK_SET) = 0 write(6, "\331\325\5\371 \241c\327\377\377\377\377\3200\257Y\0\0\0\241\0\0\2\0\0\0\4\0\0\0\0\0"..., 512) = 512 lseek(6, 512, SEEK_SET) = 512 write(6, "\0\0\0t", 4) = 4 lseek(6, 516, SEEK_SET) = 516 write(6, "\n\0\0\0\21\1\265\0\1\265\1\316\1\360\2\n\2-\2L\2k\2\234\2\315\2\357\3\16\0030"..., 1024) = 1024 lseek(6, 1540, SEEK_SET) = 1540 write(6, "\3200\257\323", 4) = 4 lseek(3, 101376, SEEK_SET) = 101376 read(3, "\n\0035\0`\1\30\0\3\371\3\362\3\353\3\344\3\335\3\326\3\317\3\310\3\301\3\272\3\263\3\254"..., 1024) = 1024 lseek(6, 1544, SEEK_SET) = 1544 write(6, "\0\0\0d", 4) = 4 lseek(6, 1548, SEEK_SET) = 1548 write(6, "\n\0035\0`\1\30\0\3\371\3\362\3\353\3\344\3\335\3\326\3\317\3\310\3\301\3\272\3\263\3\254"..., 1024) = 1024 lseek(6, 2572, SEEK_SET) = 2572 write(6, "\3200\257p", 4) = 4 lseek(6, 2576, SEEK_SET) = 2576 write(6, "\0\0\0005", 4) = 4 lseek(6, 2580, SEEK_SET) = 2580 write(6, "\n\3\226\0\35\0012\2\1d\1|\1\233\0012\1\304\1\333\1\356\2\0\2\33\1\260\2B\2M"..., 1024) = 1024 lseek(6, 3604, SEEK_SET) = 3604 write(6, "\3200\260:", 4) = 4 lseek(6, 3608, SEEK_SET) = 3608 write(6, "\0\0\0h", 4) = 4 lseek(6, 3612, SEEK_SET) = 3612 write(6, "\r\0\0\0\3\0\311\0\0\311\1\341\2\350\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 4636, SEEK_SET) = 4636 write(6, "\3200\2602", 4) = 4 lseek(6, 4640, SEEK_SET) = 4640 write(6, "\0\0\0\224", 4) = 4 lseek(6, 4644, SEEK_SET) = 4644 write(6, "\r\0\0\0\n\0F\0\3\241\3A\2\341\2\202\2\"\1\303\1d\1\5\0\245\0F\0\0\0\0"..., 1024) = 1024 lseek(6, 5668, SEEK_SET) = 5668 write(6, "\3200\260h", 4) = 4 lseek(6, 5672, SEEK_SET) = 5672 write(6, "\0\0\0\241", 4) = 4 lseek(6, 5676, SEEK_SET) = 5676 write(6, "\r\3(\0\2\1\301\0\1\301\2\244\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 6700, SEEK_SET) = 6700 write(6, "\3200\257\313", 4) = 4 lseek(6, 6704, SEEK_SET) = 6704 write(6, "\0\0\0\240", 4) = 4 lseek(6, 6708, SEEK_SET) = 6708 write(6, "\r\0\0\0\5\2\"\0\3\241\3B\2\342\2\202\2\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 7732, SEEK_SET) = 7732 write(6, "\3200\257\304", 4) = 4 lseek(3, 3072, SEEK_SET) = 3072 read(3, "\r\0\0\0\3\3\325\0\3\361\3\346\3\325\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 7736, SEEK_SET) = 7736 write(6, "\0\0\0\25", 4) = 4 lseek(6, 7740, SEEK_SET) = 7740 write(6, "\r\0\0\0\0\4\0\0\2\230\2\230\2\230\2\230\2\230\2\230\2\230\2\230\2\230\1\232\1\232\1\232"..., 1024) = 1024 lseek(6, 8764, SEEK_SET) = 8764 write(6, "\3200\257[", 4) = 4 lseek(6, 8768, SEEK_SET) = 8768 write(6, "\0\0\0\4", 4) = 4 lseek(6, 8772, SEEK_SET) = 8772 write(6, "\r\0\0\0\3\3\325\0\3\361\3\346\3\325\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 9796, SEEK_SET) = 9796 write(6, "\3200\257Y", 4) = 4 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 lseek(6, 9800, SEEK_SET) = 9800 write(6, "\0\0\0\1", 4) = 4 lseek(6, 9804, SEEK_SET) = 9804 write(6, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\242\0\0\0\241"..., 1024) = 1024 lseek(6, 10828, SEEK_SET) = 10828 write(6, "\3200\257Y", 4) = 4 lseek(3, 0, SEEK_SET) = 0 write(3, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\243\0\0\0\241"..., 1024) = 1024 lseek(3, 3072, SEEK_SET) = 3072 write(3, "\r\0\0\0\3\3\325\0\3\361\3\346\3\325\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 20480, SEEK_SET) = 20480 write(3, "\r\0\0\0\1\3\331\0\3\331\2\230\2\230\2\230\2\230\2\230\2\230\2\230\2\230\1\232\1\232\1\232"..., 1024) = 1024 lseek(3, 53248, SEEK_SET) = 53248 write(3, "\n\3\226\0\35\0012\2\1d\1|\1\233\0012\1\304\1\333\1\356\2\0\2\33\1\260\2B\2M"..., 1024) = 1024 lseek(3, 101376, SEEK_SET) = 101376 write(3, "\n\0035\0`\1\30\0\3\371\3\362\3\353\3\344\3\335\3\326\3\317\3\310\3\301\3\272\3\263\3\254"..., 1024) = 1024 lseek(3, 105472, SEEK_SET) = 105472 write(3, "\r\2\350\0\2\0\311\0\0\311\1\341\2\350\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 117760, SEEK_SET) = 117760 write(3, "\n\0\0\0\21\1\265\0\1\265\1\316\1\360\2\n\2-\2L\2k\2\234\2\315\2\357\3\16\0030"..., 1024) = 1024 lseek(3, 150528, SEEK_SET) = 150528 write(3, "\r\0\0\0\n\0F\0\3\241\3A\2\341\2\202\2\"\1\303\1d\1\5\0\245\0F\0\0\0\0"..., 1024) = 1024 lseek(3, 162816, SEEK_SET) = 162816 write(3, "\r\0\0\0\5\2\"\0\3\241\3B\2\342\2\202\2\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 163840, SEEK_SET) = 163840 write(3, "\r\3(\0\3\0\264\0\1\301\2\244\0\264\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 close(6) = 0 unlink("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal") = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=2}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.pdf", {st_mode=S_IFREG|0664, st_size=438348, ...}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\243\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\243\0\0\0\241\0\0\0v\0\0\0\1", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741825, len=1}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", O_RDWR|O_CREAT|O_CLOEXEC, 0644) = 6 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 geteuid() = 530 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 lseek(6, 0, SEEK_SET) = 0 write(6, "\331\325\5\371 \241c\327\377\377\377\377{)\253\354\0\0\0\241\0\0\2\0\0\0\4\0\0\0\0\0"..., 512) = 512 lseek(6, 512, SEEK_SET) = 512 write(6, "\0\0\0t", 4) = 4 lseek(6, 516, SEEK_SET) = 516 write(6, "\n\0\0\0\21\1\265\0\1\265\1\316\1\360\2\n\2-\2L\2k\2\234\2\315\2\357\3\16\0030"..., 1024) = 1024 lseek(6, 1540, SEEK_SET) = 1540 write(6, "{)\254f", 4) = 4 lseek(6, 1544, SEEK_SET) = 1544 write(6, "\0\0\0d", 4) = 4 lseek(6, 1548, SEEK_SET) = 1548 write(6, "\n\0035\0`\1\30\0\3\371\3\362\3\353\3\344\3\335\3\326\3\317\3\310\3\301\3\272\3\263\3\254"..., 1024) = 1024 lseek(6, 2572, SEEK_SET) = 2572 write(6, "{)\254\3", 4) = 4 lseek(6, 2576, SEEK_SET) = 2576 write(6, "\0\0\0005", 4) = 4 lseek(6, 2580, SEEK_SET) = 2580 write(6, "\n\3\226\0\35\0012\2\1d\1|\1\233\0012\1\304\1\333\1\356\2\0\2\33\1\260\2B\2M"..., 1024) = 1024 lseek(6, 3604, SEEK_SET) = 3604 write(6, "{)\254\315", 4) = 4 lseek(6, 3608, SEEK_SET) = 3608 write(6, "\0\0\0h", 4) = 4 lseek(6, 3612, SEEK_SET) = 3612 write(6, "\r\2\350\0\2\0\311\0\0\311\1\341\2\350\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 4636, SEEK_SET) = 4636 write(6, "{)\254S", 4) = 4 lseek(6, 4640, SEEK_SET) = 4640 write(6, "\0\0\0\30", 4) = 4 lseek(6, 4644, SEEK_SET) = 4644 write(6, "\5\3~\0$\3\25\f\0\0\0\241\3\265\3\260\3L\3X\3\246\3\241\3\234\3\227\3\222\3\210"..., 1024) = 1024 lseek(6, 5668, SEEK_SET) = 5668 write(6, "{)\254\26", 4) = 4 lseek(3, 139264, SEEK_SET) = 139264 read(3, "\r\2i\0\2\1\237\0\1\237\0030\0039\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 5672, SEEK_SET) = 5672 write(6, "\0\0\0\211", 4) = 4 lseek(6, 5676, SEEK_SET) = 5676 write(6, "\r\2i\0\2\1\237\0\1\237\0030\0039\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 6700, SEEK_SET) = 6700 write(6, "{)\254\2", 4) = 4 lseek(6, 6704, SEEK_SET) = 6704 write(6, "\0\0\0\1", 4) = 4 lseek(6, 6708, SEEK_SET) = 6708 write(6, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\243\0\0\0\241"..., 1024) = 1024 lseek(6, 7732, SEEK_SET) = 7732 write(6, "{)\253\354", 4) = 4 lseek(6, 7736, SEEK_SET) = 7736 write(6, "\0\0\0\222", 4) = 4 lseek(6, 7740, SEEK_SET) = 7740 write(6, "\r\0\0\0\2\2\202\0\2\202\3\17\3\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 8764, SEEK_SET) = 8764 write(6, "{)\254\32", 4) = 4 lseek(3, 119808, SEEK_SET) = 119808 read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 8768, SEEK_SET) = 8768 write(6, "\0\0\0v", 4) = 4 lseek(6, 8772, SEEK_SET) = 8772 write(6, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 9796, SEEK_SET) = 9796 write(6, "{)\253\354", 4) = 4 lseek(6, 9800, SEEK_SET) = 9800 write(6, "\0\0\0\217", 4) = 4 lseek(6, 9804, SEEK_SET) = 9804 write(6, "\r\0\0\0\n\0F\0\3\241\3A\2\342\2\203\2#\1\303\1d\1\5\0\246\0F\0\0\0\0"..., 1024) = 1024 lseek(6, 10828, SEEK_SET) = 10828 write(6, "{)\254\372", 4) = 4 lseek(6, 10832, SEEK_SET) = 10832 write(6, "\0\0\0\241", 4) = 4 lseek(6, 10836, SEEK_SET) = 10836 write(6, "\r\3(\0\3\0\264\0\1\301\2\244\0\264\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 11860, SEEK_SET) = 11860 write(6, "{)\254\376", 4) = 4 lseek(6, 11864, SEEK_SET) = 11864 write(6, "\0\0\0\240", 4) = 4 lseek(6, 11868, SEEK_SET) = 11868 write(6, "\r\0\0\0\5\2\"\0\3\241\3B\2\342\2\202\2\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 12892, SEEK_SET) = 12892 write(6, "{)\254W", 4) = 4 lseek(6, 12896, SEEK_SET) = 12896 write(6, "\0\0\0\25", 4) = 4 lseek(6, 12900, SEEK_SET) = 12900 write(6, "\r\0\0\0\1\3\331\0\3\331\2\230\2\230\2\230\2\230\2\230\2\230\2\230\2\230\1\232\1\232\1\232"..., 1024) = 1024 lseek(6, 13924, SEEK_SET) = 13924 write(6, "{)\253\356", 4) = 4 lseek(6, 13928, SEEK_SET) = 13928 write(6, "\0\0\0\4", 4) = 4 lseek(6, 13932, SEEK_SET) = 13932 write(6, "\r\0\0\0\3\3\325\0\3\361\3\346\3\325\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 14956, SEEK_SET) = 14956 write(6, "{)\253\354", 4) = 4 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 lseek(3, 0, SEEK_SET) = 0 write(3, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\244\0\0\0\241"..., 1024) = 1024 lseek(3, 3072, SEEK_SET) = 3072 write(3, "\r\0\0\0\3\3\325\0\3\361\3\346\3\325\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 20480, SEEK_SET) = 20480 write(3, "\r\0\0\0\2\3\262\0\3\331\3\262\2\230\2\230\2\230\2\230\2\230\2\230\2\230\1\232\1\232\1\232"..., 1024) = 1024 lseek(3, 23552, SEEK_SET) = 23552 write(3, "\5\3~\0#\3\25\f\0\0\0\241\3\265\3\260\3L\3X\3\246\3\241\3\234\3\227\3\222\3\210"..., 1024) = 1024 lseek(3, 53248, SEEK_SET) = 53248 write(3, "\n\3\226\0\35\0012\2\1d\1|\1\233\0012\1\304\1\333\1\356\2\0\2\33\1\260\2B\2M"..., 1024) = 1024 lseek(3, 101376, SEEK_SET) = 101376 write(3, "\n\0035\0`\1\30\0\3\371\3\362\3\353\3\344\3\335\3\326\3\317\3\310\3\301\3\272\3\263\3\254"..., 1024) = 1024 lseek(3, 105472, SEEK_SET) = 105472 write(3, "\r\0\0\0\3\1_\0\1_\2)\2\371\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 117760, SEEK_SET) = 117760 write(3, "\n\0\0\0\21\1\265\0\1\265\1\316\1\360\2\n\2-\2L\2k\2\234\2\315\2\357\3\16\0030"..., 1024) = 1024 lseek(3, 119808, SEEK_SET) = 119808 write(3, "\0\0\0\0\0\0\0\1\0\0\0\222\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 139264, SEEK_SET) = 139264 write(3, "\r\0\0\0\2\2\202\0\2\202\3\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 145408, SEEK_SET) = 145408 write(3, "\r\0\0\0\n\0F\0\3\241\3A\2\342\2\203\2#\1\303\1d\1\5\0\246\0F\0\0\0\0"..., 1024) = 1024 lseek(3, 148480, SEEK_SET) = 148480 write(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 162816, SEEK_SET) = 162816 write(3, "\r\0\0\0\5\2\"\0\3\241\3B\2\342\2\202\2\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 163840, SEEK_SET) = 163840 write(3, "\r\0\0\0\4\0\177\0\3\35\2\231\1\214\0\177\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 close(6) = 0 unlink("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal") = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=2}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\244\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.odt", {st_mode=S_IFREG|0664, st_size=197896, ...}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\244\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\244\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\244\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\244\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.odt", {st_mode=S_IFREG|0664, st_size=197896, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.odt", {st_mode=S_IFREG|0664, st_size=197896, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.odt", {st_mode=S_IFREG|0664, st_size=197896, ...}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\244\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741825, len=1}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", O_RDWR|O_CREAT|O_CLOEXEC, 0644) = 6 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 geteuid() = 530 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 lseek(6, 0, SEEK_SET) = 0 write(6, "\331\325\5\371 \241c\327\377\377\377\377h\201\256\326\0\0\0\241\0\0\2\0\0\0\4\0\0\0\0\0"..., 512) = 512 lseek(6, 512, SEEK_SET) = 512 write(6, "\0\0\0\241", 4) = 4 lseek(6, 516, SEEK_SET) = 516 write(6, "\r\0\0\0\4\0\177\0\3\35\2\231\1\214\0\177\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 1540, SEEK_SET) = 1540 write(6, "h\201\257{", 4) = 4 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 lseek(6, 1544, SEEK_SET) = 1544 write(6, "\0\0\0\1", 4) = 4 lseek(6, 1548, SEEK_SET) = 1548 write(6, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\244\0\0\0\241"..., 1024) = 1024 lseek(6, 2572, SEEK_SET) = 2572 write(6, "h\201\256\326", 4) = 4 lseek(3, 0, SEEK_SET) = 0 write(3, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\245\0\0\0\241"..., 1024) = 1024 lseek(3, 163840, SEEK_SET) = 163840 write(3, "\r\0\0\0\4\0t\0\3\35\2\231\0t\1\214\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 close(6) = 0 unlink("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal") = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=2}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\245\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741825, len=1}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", O_RDWR|O_CREAT|O_CLOEXEC, 0644) = 6 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 geteuid() = 530 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 lseek(6, 0, SEEK_SET) = 0 write(6, "\331\325\5\371 \241c\327\377\377\377\377\360IZ\206\0\0\0\241\0\0\2\0\0\0\4\0\0\0\0\0"..., 512) = 512 lseek(6, 512, SEEK_SET) = 512 write(6, "\0\0\0\25", 4) = 4 lseek(6, 516, SEEK_SET) = 516 write(6, "\r\0\0\0\2\3\262\0\3\331\3\262\2\230\2\230\2\230\2\230\2\230\2\230\2\230\1\232\1\232\1\232"..., 1024) = 1024 lseek(6, 1540, SEEK_SET) = 1540 write(6, "\360IZ\210", 4) = 4 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 lseek(6, 1544, SEEK_SET) = 1544 write(6, "\0\0\0\1", 4) = 4 lseek(6, 1548, SEEK_SET) = 1548 write(6, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\245\0\0\0\241"..., 1024) = 1024 lseek(6, 2572, SEEK_SET) = 2572 write(6, "\360IZ\206", 4) = 4 lseek(3, 0, SEEK_SET) = 0 write(3, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\246\0\0\0\241"..., 1024) = 1024 lseek(3, 20480, SEEK_SET) = 20480 write(3, "\r\3\331\0\1\3\262\0\3\262\3\262\2\230\2\230\2\230\2\230\2\230\2\230\2\230\1\232\1\232\1\232"..., 1024) = 1024 close(6) = 0 unlink("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal") = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=2}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.pdf", {st_mode=S_IFREG|0664, st_size=438348, ...}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\246\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\246\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\246\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\246\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.pdf", {st_mode=S_IFREG|0664, st_size=438348, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.pdf", {st_mode=S_IFREG|0664, st_size=438348, ...}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/doc/aprx-manual.pdf", {st_mode=S_IFREG|0664, st_size=438348, ...}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\246\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741825, len=1}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", O_RDWR|O_CREAT|O_CLOEXEC, 0644) = 6 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 geteuid() = 530 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 lseek(6, 0, SEEK_SET) = 0 write(6, "\331\325\5\371 \241c\327\377\377\377\377\314\357S\363\0\0\0\241\0\0\2\0\0\0\4\0\0\0\0\0"..., 512) = 512 lseek(6, 512, SEEK_SET) = 512 write(6, "\0\0\0\241", 4) = 4 lseek(6, 516, SEEK_SET) = 516 write(6, "\r\0\0\0\4\0t\0\3\35\2\231\0t\1\214\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 1540, SEEK_SET) = 1540 write(6, "\314\357T\316", 4) = 4 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 lseek(6, 1544, SEEK_SET) = 1544 write(6, "\0\0\0\1", 4) = 4 lseek(6, 1548, SEEK_SET) = 1548 write(6, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\246\0\0\0\241"..., 1024) = 1024 lseek(6, 2572, SEEK_SET) = 2572 write(6, "\314\357S\363", 4) = 4 lseek(3, 0, SEEK_SET) = 0 write(3, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\247\0\0\0\241"..., 1024) = 1024 lseek(3, 163840, SEEK_SET) = 163840 write(3, "\r\0\0\0\4\0i\0\3\35\2\231\1\201\0i\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 close(6) = 0 unlink("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal") = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=2}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\247\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741825, len=1}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", O_RDWR|O_CREAT|O_CLOEXEC, 0644) = 6 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 geteuid() = 530 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 lseek(6, 0, SEEK_SET) = 0 write(6, "\331\325\5\371 \241c\327\377\377\377\377\345u\251\254\0\0\0\241\0\0\2\0\0\0\4\0\0\0\0\0"..., 512) = 512 lseek(6, 512, SEEK_SET) = 512 write(6, "\0\0\0\25", 4) = 4 lseek(6, 516, SEEK_SET) = 516 write(6, "\r\3\331\0\1\3\262\0\3\262\3\262\2\230\2\230\2\230\2\230\2\230\2\230\2\230\1\232\1\232\1\232"..., 1024) = 1024 lseek(6, 1540, SEEK_SET) = 1540 write(6, "\345u\251\256", 4) = 4 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 lseek(6, 1544, SEEK_SET) = 1544 write(6, "\0\0\0\1", 4) = 4 lseek(6, 1548, SEEK_SET) = 1548 write(6, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\247\0\0\0\241"..., 1024) = 1024 lseek(6, 2572, SEEK_SET) = 2572 write(6, "\345u\251\254", 4) = 4 lseek(3, 0, SEEK_SET) = 0 write(3, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\250\0\0\0\241"..., 1024) = 1024 lseek(3, 20480, SEEK_SET) = 20480 write(3, "\r\0\0\0\0\4\0\0\3\262\3\262\2\230\2\230\2\230\2\230\2\230\2\230\2\230\1\232\1\232\1\232"..., 1024) = 1024 close(6) = 0 unlink("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal") = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=2}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 lstat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 select(0, NULL, NULL, NULL, {0, 1000}) = 0 (Timeout) fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\250\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 lseek(3, 24, SEEK_SET) = 24 read(3, "\0\0\5\250\0\0\0\241\0\0\0v\0\0\0\2", 16) = 16 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 access("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-wal", F_OK) = -1 ENOENT (No such file or directory) fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741825, len=1}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 open("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal", O_RDWR|O_CREAT|O_CLOEXEC, 0644) = 6 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 geteuid() = 530 fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 lseek(6, 0, SEEK_SET) = 0 write(6, "\331\325\5\371 \241c\327\377\377\377\377m)H%\0\0\0\241\0\0\2\0\0\0\4\0\0\0\0\0"..., 512) = 512 lseek(6, 512, SEEK_SET) = 512 write(6, "\0\0\0\27", 4) = 4 lseek(6, 516, SEEK_SET) = 516 write(6, "\n\0\0\0\1\3\373\0\3\373\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 1540, SEEK_SET) = 1540 write(6, "m)H%", 4) = 4 lseek(6, 1544, SEEK_SET) = 1544 write(6, "\0\0\0\26", 4) = 4 lseek(6, 1548, SEEK_SET) = 1548 write(6, "\r\0\0\0\1\3\371\0\3\371\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(6, 2572, SEEK_SET) = 2572 write(6, "m)H%", 4) = 4 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741824, len=1}) = 0 fcntl(3, F_SETLK, {type=F_WRLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 lseek(6, 2576, SEEK_SET) = 2576 write(6, "\0\0\0\1", 4) = 4 lseek(6, 2580, SEEK_SET) = 2580 write(6, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\250\0\0\0\241"..., 1024) = 1024 lseek(6, 3604, SEEK_SET) = 3604 write(6, "m)H%", 4) = 4 lseek(3, 0, SEEK_SET) = 0 write(3, "SQLite format 3\0\4\0\1\1\0@ \0\0\5\251\0\0\0\241"..., 1024) = 1024 lseek(3, 21504, SEEK_SET) = 21504 write(3, "\r\0\0\0\0\4\0\0\3\371\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 22528, SEEK_SET) = 22528 write(3, "\n\0\0\0\0\4\0\0\3\373\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 close(6) = 0 unlink("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db-journal") = 0 fcntl(3, F_SETLK, {type=F_RDLCK, whence=SEEK_SET, start=1073741826, len=510}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=1073741824, len=2}) = 0 fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0 close(5) = 0 close(-1) = -1 EBADF (Bad file descriptor) close(4) = 0 fstat(3, {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 stat("/net/fileserver.methics.fi/mnt/mirror/common/scratch/mea/ham/aprx/aprx-trunk/.svn/wc.db", {st_mode=S_IFREG|0644, st_size=164864, ...}) = 0 close(3) = 0 munmap(0x7fb0c4174000, 212992) = 0 munmap(0x7fb0c41d2000, 139264) = 0 munmap(0x7fb0c4152000, 139264) = 0 exit_group(0) = ? +++ exited with 0 +++ aprx-2.08.svn593/config.c0000644000175000017500000005004012313325035014062 0ustar colincolin/* **************************************************************** * * * * APRX -- 2nd generation receive-only APRS-i-gate with * * minimal requirement of esoteric facilities or * * libraries of any kind beyond UNIX system libc. * * * * (c) Matti Aarnio - OH2MQK, 2007-2014 * * * * **************************************************************** */ #include "aprx.h" #ifndef DISABLE_IGATE const char *aprsis_login; #endif char *config_SKIPSPACE(char *Y) { assert(Y != NULL); while (*Y == ' ' || *Y == '\t') ++Y; return Y; } #if 0 char *config_SKIPDIGIT(char *Y) { assert(Y != NULL); while ('0' <= *Y && *Y <= '9') ++Y; return Y; } #endif // return 0 for failures, 1 for OK. int validate_callsign_input(char *callsign, int strict) { int i = strlen(callsign); char *p = callsign; char c = 0; int seen_minus = 0; int ssid = 0; for ( ; *p ; ++p ) { c = *p; if ('a' <= c && c <= 'z') { // Presuming ASCII c -= ('a'-'A'); *p = c; // Side-effect: translates the callsign to uppercase } if (!seen_minus && c == '-') { if (p == callsign || p[1] == 0) return 0; // Hyphen is at beginning or at end! if ((p - callsign) > 6) return 0; // Hyphen too far! Max 6 characters preceding it. seen_minus = 1; continue; } else if (seen_minus && c == '-') { return 0; // Seen a hyphen again! } // The "SSID" value can be alphanumeric here! if (!seen_minus) { // Callsign prefix if (('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')) { // Valid character! } else { return 0; // Invalid characters in callsign part } } else { // SSID tail if (strict) { if ('0' <= c && c <= '9') { // Valid character! ssid = ssid * 10 + c - '0'; if (ssid > 15) { // SSID value range: 0 .. 15 return 0; } } else { return 0; // Invalid characters in SSID part } } else { // non-strict if (('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')) { // Valid character! } else { return 0; // Invalid characters in SSID part } } } } if (i > 3 && (callsign[i - 1] == '0' && callsign[i - 2] == '-')) { callsign[i - 2] = 0; /* If tailed with "-0" SSID, chop it off.. */ } return 1; } /* SKIPTEXT: * * Detect "/' -> scan until matching double quote * Process \-escapes on string: \xFD, \n, \", \' * Detect non-eol, non-space(tab): scan until eol, or white-space * No \-escapes * * Will thus stop when found non-quoted space/tab, or * end of line/string. */ char *config_SKIPTEXT(char *Y, int *lenp) { char *O; char endc; int len; assert(Y != NULL); O = Y; endc = *Y; len = 0; if (*Y == '"' || *Y == '\'') { ++Y; while (*Y && *Y != endc) { if (*Y == '\\') { /* backslash escape.. */ ++Y; if (*Y == 'n') { *O++ = '\n'; ++len; } else if (*Y == 'r') { *O++ = '\r'; ++len; } else if (*Y == '"') { *O++ = '"'; ++len; } else if (*Y == '\'') { *O++ = '\''; } else if (*Y == '\\') { *O++ = '\\'; } else if (*Y == 'x') { /* Hex encoded char */ int i; char hx[3]; if (*Y) ++Y; hx[0] = *Y; if (*Y) ++Y; hx[1] = *Y; hx[2] = 0; i = (int) strtol(hx, NULL, 16); *O++ = i; ++len; } } else { *O++ = *Y; ++len; } if (*Y != 0) ++Y; } if (*Y == endc) ++Y; *O = 0; /* String end */ /* STOP at the tail-end " */ } else { while (*Y && *Y != ' ' && *Y != '\t') { ++Y; ++len; } /* Stop at white-space or end */ if (*Y) *Y++ = 0; } if (lenp != NULL) *lenp = len; return Y; } void config_STRLOWER(char *s) { int c; assert(s != NULL); for (; *s; ++s) { c = *s; if ('A' <= c && c <= 'Z') { *s = c + ('a' - 'A'); } } } void config_STRUPPER(char *s) { int c; assert(s != NULL); for (; *s; ++s) { c = *s; if ('a' <= c && c <= 'z') { *s = c + ('A' - 'a'); } } } static int logging_config(struct configfile *cf) { char *name, *param1; char *str = cf->buf; int has_fault = 0; while (readconfigline(cf) != NULL) { if (configline_is_comment(cf)) continue; /* Comment line, or empty line */ str = cf->buf; str = config_SKIPSPACE(str); // arbitrary indention name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRLOWER(name); param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (strcmp(name, "") == 0) break; if (strcmp(name, "aprxlog") == 0) { if (debug) printf("%s:%d: INFO: APRXLOG = '%s' '%s'\n", cf->name, cf->linenum, param1, str); aprxlogfile = strdup(param1); #ifndef DISABLE_IGATE } else if (strcmp(name, "dprslog") == 0) { if (debug) printf("%s:%d: INFO: DPRSLOG = '%s' '%s'\n", cf->name, cf->linenum, param1, str); dprslogfile = strdup(param1); #endif } else if (strcmp(name, "rflog") == 0) { if (debug) printf("%s:%d: INFO: RFLOG = '%s' '%s'\n", cf->name, cf->linenum, param1, str); rflogfile = strdup(param1); } else if (strcmp(name, "pidfile") == 0) { if (debug) printf("%s:%d: INFO: PIDFILE = '%s' '%s'\n", cf->name, cf->linenum, param1, str); pidfile = strdup(param1); } else if (strcmp(name, "erlangfile") == 0) { if (debug) printf("%s:%d: INFO: ERLANGFILE = '%s' '%s'\n", cf->name, cf->linenum, param1, str); erlang_backingstore = strdup(param1); } else if (strcmp(name, "erlang-loglevel") == 0) { if (debug) printf("%s:%d: INFO: ERLANG-LOGLEVEL = '%s' '%s'\n", cf->name, cf->linenum, param1, str); erlang_init(param1); } else if (strcmp(name, "erlanglog") == 0) { if (debug) printf("%s:%d: INFO: ERLANGLOG = '%s'\n", cf->name, cf->linenum, param1); erlanglogfile = strdup(param1); } else if (strcmp(name, "erlang-log1min") == 0) { if (debug) printf("%s:%d: INFO: ERLANG-LOG1MIN\n", cf->name, cf->linenum); erlanglog1min = 1; } else { printf("%s:%d: ERROR: Unknown keyword: '%s' '%s'\n", cf->name, cf->linenum, name, param1); has_fault = 1; } } return has_fault; } static int cfgparam(struct configfile *cf) { char *name, *param1; char *str = cf->buf; str = config_SKIPSPACE(str); // arbitrary indention name = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); config_STRLOWER(name); param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); #ifndef DISABLE_IGATE if (strcmp(name, "") == 0) { return aprsis_config(cf); } #endif if (strcmp(name, "") == 0) { return interface_config(cf); } if (strcmp(name, "") == 0) { return telemetry_config(cf); } if (strcmp(name, "") == 0) { return digipeater_config(cf); } if (strcmp(name, "") == 0) { return beacon_config(cf); } if (strcmp(name, "") == 0) { return logging_config(cf); } if (strcmp(name, "mycall") == 0) { config_STRUPPER(param1); // Store these always, it helps with latter error diagnostics mycall = strdup(param1); #ifndef DISABLE_IGATE aprsis_login = mycall; #endif if (validate_callsign_input(param1,1)) { if (debug) printf("%s:%d: MYCALL = '%s' '%s'\n", cf->name, cf->linenum, mycall, str); } else { if (validate_callsign_input(param1,0)) { printf("%s:%d: MYCALL = '%s' value is OK for APRSIS login, and Rx-IGate, but not valid AX.25 node callsign.\n", cf->name, cf->linenum, param1); } else { // but sometimes the parser yields an error! printf("%s:%d: MYCALL = '%s' value is not valid AX.25 node callsign, nor valid for APRSIS login.\n", cf->name, cf->linenum, param1); return 1; } } } else if (strcmp(name, "myloc") == 0) { // lat xx lon yy char *latp; char *lonp; float lat, lng; int i, la, lo; char lac, loc; const char *const errmsg = "%s:%d: myloc parameters wrong, expected format: 'myloc' 'lat' 'ddmm.mmN' 'lon' 'dddmm.mmE'\n"; if (strcmp(param1, "lat") != 0) { printf(errmsg, cf->name, cf->linenum); printf(" .. 'lat' missing, got: '%s'\n", param1); return 1; } latp = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); param1 = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (strcmp(param1, "lon") != 0) { printf(errmsg, cf->name, cf->linenum); printf(" .. 'lon' missing, got: '%s'\n", param1); return 1; } lonp = str; str = config_SKIPTEXT(str, NULL); str = config_SKIPSPACE(str); if (validate_degmin_input(latp, 90)) { printf(errmsg, cf->name, cf->linenum); printf(" got lat: '%s'\n", latp); return 1; } if (validate_degmin_input(lonp, 180)) { printf(errmsg, cf->name, cf->linenum); printf(" got lon: '%s'\n", lonp); return 1; } i = sscanf(latp, "%2d%5f%c,", &la, &lat, &lac); if (i != 3) { printf(errmsg, cf->name, cf->linenum); printf(" got parse-field-count: %d on '%s'\n", i, latp); return 1; // parse failure } i = sscanf(lonp, "%3d%5f%c,", &lo, &lng, &loc); if (i != 3) { printf(errmsg, cf->name, cf->linenum); printf(" got parse-field-count: %d on '%s'\n", i, lonp); return 1; // parse failure } if (lac != 'N' && lac != 'S' && lac != 'n' && lac != 's') { printf(errmsg, cf->name, cf->linenum); printf(" .. lat expected N/S tail, got: '%c'\n", lac); return 1; // bad indicator value } if (loc != 'E' && loc != 'W' && loc != 'e' && loc != 'w') { printf(errmsg, cf->name, cf->linenum); printf(" .. lon expected E/W tail, got: '%c'\n", loc); return 1; // bad indicator value } myloc_latstr = strdup(latp); myloc_lonstr = strdup(lonp); myloc_lat = (float)la + lat/60.0; myloc_lon = (float)lo + lng/60.0; if (lac == 'S' || lac == 's') myloc_lat = -myloc_lat; if (loc == 'W' || loc == 'w') myloc_lon = -myloc_lon; if (debug) printf("%s:%d: MYLOC LAT %8.5f degrees LON %8.5f degrees\n", cf->name, cf->linenum, myloc_lat, myloc_lon); myloc_lat = filter_lat2rad(myloc_lat); myloc_lon = filter_lon2rad(myloc_lon); myloc_coslat = cos(myloc_lat); #ifndef DISABLE_IGATE } else if (strcmp(name, "aprsis-login") == 0) { printf("%s:%d WARNING: Old-style top-level 'aprsis-login' definition, it should be inside group tags.\n", cf->name, cf->linenum); config_STRUPPER(param1); aprsis_login = strdup(param1); if (validate_callsign_input(param1,0)) { if (debug) printf("%s:%d: APRSIS-LOGIN = '%s' '%s'\n", cf->name, cf->linenum, aprsis_login, str); } else { printf("%s:%d: APRSIS-LOGIN = '%s' value is not valid AX25-like node'\n", cf->name, cf->linenum, aprsis_login); return 1; } } else if (strcmp(name, "aprsis-server") == 0) { printf("%s:%d WARNING: Old-style top-level 'aprsis-server' definition, it should be inside group tags.\n", cf->name, cf->linenum); if (debug) printf("%s:%d: APRSIS-SERVER = '%s':'%s'\n", cf->name, cf->linenum, param1, str); return aprsis_add_server(param1, str); } else if (strcmp(name, "aprsis-heartbeat-timeout") == 0) { int i = atoi(param1); if (i < 0) /* param failure ? */ i = 0; /* no timeout */ printf("%s:%d WARNING: Old-style top-level 'aprsis-heartbeat-timeout' definition, it should be inside group tags.\n", cf->name, cf->linenum); if (debug) printf("%s:%d: APRSIS-HEARTBEAT-TIMEOUT = '%d' '%s'\n", cf->name, cf->linenum, i, str); return aprsis_set_heartbeat_timeout(i); } else if (strcmp(name, "aprsis-filter") == 0) { printf("%s:%d WARNING: Old-style top-level 'aprsis-filter' definition, it should be inside group tags.\n", cf->name, cf->linenum); return aprsis_set_filter(param1); #endif #ifdef PF_AX25 /* PF_AX25 exists -- highly likely a Linux system ! */ } else if (strcmp(name, "ax25-rxport") == 0) { printf("%s:%d WARNING: Old-style top-level 'ax25-rxport' definition. See groups, 'ax25-device' definitions.\n", cf->name, cf->linenum); if (debug) printf("%s:%d: AX25-RXPORT '%s' '%s'\n", cf->name, cf->linenum, param1, str); return (netax25_addrxport(param1, NULL) == NULL); #endif } else if (strcmp(name, "radio") == 0) { printf("%s:%d WARNING: Old-style top-level 'radio' definition. See groups, 'serial-device' or 'tcp-device' definitions.\n", cf->name, cf->linenum); if (debug) printf("%s:%d: RADIO = %s %s..\n", cf->name, cf->linenum, param1, str); return (ttyreader_serialcfg(cf, param1, str) == NULL); } else if (strcmp(name, "ax25-device") == 0) { printf("%s:%d ERROR: The 'ax25-device' entry must be inside an group tag.\n", cf->name, cf->linenum); return 1; } else if (strcmp(name, "serial-device") == 0) { printf("%s:%d ERROR: The 'serial-device' entry must be inside an group tag.\n", cf->name, cf->linenum); return 1; } else if (strcmp(name, "tcp-device") == 0) { printf("%s:%d ERROR: The 'tcp-device' entry must be inside an group tag.\n", cf->name, cf->linenum); return 1; } else if (strcmp(name, "beacon") == 0) { printf("%s:%d ERROR: The 'beacon' entry must be inside a group tag.\n", cf->name, cf->linenum); return 1; } else { printf("%s:%d: ERROR: Unknown config keyword: '%s' '%s'\n", cf->name, cf->linenum, name, param1); printf("%s:%d: Perhaps this is due to lack of some surrounding tag ?\n", cf->name, cf->linenum); return 1; } return 0; } const char* scan_int(const char *p, int len, int *val, int *seen_space) { int i; char c; *val = 0; for (i = 0; i < len; ++i, ++p) { c = *p; if (('0' <= c && c <= '9') && !(*seen_space)) { *val = (*val) * 10 + (c - '0'); } else if (c == ' ') { *val = (*val) * 10; *seen_space = 1; } else { return NULL; } } return p; } int validate_degmin_input(const char *s, int maxdeg) { int deg; int m1, m2; char c; const char *t; int seen_space = 0; if (maxdeg > 90) { t = scan_int(s, 3, °, &seen_space); if (t != s+3) return 1; // scan failure if (deg > 179) return 1; // too large value s = t; t = scan_int(s, 2, &m1, &seen_space); if (t != s+2) return 1; if (m1 > 59) return 1; s = t; c = *s; if (!seen_space && c == '.') { // OK } else if (!seen_space && c == ' ') { seen_space = 1; } else { return 1; // Bad char.. } ++s; t = scan_int(s, 2, &m2, &seen_space); if (t != s+2) return 1; s = t; c = *s; if (c != 'E' && c != 'e' && c != 'W' && c != 'w') return 1; } else { t = scan_int(s, 2, °, &seen_space); if (t != s+2) return 1; // scan failure if (deg > 89) return 1; // too large value s = t; t = scan_int(s, 2, &m1, &seen_space); if (t != s+2) return 1; if (m1 > 59) return 1; s = t; c = *s; if (!seen_space && c == '.') { // OK } else if (!seen_space && c == ' ') { seen_space = 1; } else { return 1; // Bad char.. } ++s; t = scan_int(s, 2, &m2, &seen_space); if (t != s+2) return 1; s = t; c = *s; if (c != 'N' && c != 'n' && c != 'S' && c != 's') return 1; } return 0; /* zero for OK */ } /* * This interval parser is originally from ZMailer MTA. * Slightly expanded to permit white-spaces inside the string. * (c) Matti Aarnio, Rayan Zachariassen.. */ static int parse_interval(const char *string, const char **restp) { int intvl = 0; int val; int c; for (; *string; ++string) { val = 0; c = *string; while ('0' <= c && c <= '9') { val = val * 10 + (c - '0'); c = *++string; } switch (c) { case 'd': /* days */ case 'D': /* days */ val *= (24*60*60); break; case 'h': /* hours */ case 'H': /* hours */ val *= 60*60; break; case 'm': /* minutes */ case 'M': /* minutes */ val *= 60; break; case 's': /* seconds */ case 'S': /* seconds */ /* val *= 1; */ case '\t': /* just whitespace */ case ' ': /* just whitespace */ break; default: /* Not of: "dhms" - maybe string end, maybe junk ? */ if (restp) *restp = string; return intvl + val; } intvl += val; } if (restp) *restp = string; return intvl; } // Return 0 on OK, != 0 on error int config_parse_interval(const char *par, int *resultp) { const char *rest = NULL; int ret = parse_interval(par, &rest); if (*rest != 0) return 1; // Did not consume whole input string *resultp = ret; return 0; } // Return 0 on OK, != 0 on error int config_parse_boolean(const char *par, int *resultp) { if (strcasecmp(par, "true") == 0 || strcmp(par, "1") == 0 || strcasecmp(par, "yes") == 0 || strcasecmp(par, "on") == 0 || strcasecmp(par, "y") == 0) { *resultp = 1; return 1; } else if (strcasecmp(par, "false") == 0 || strcmp(par, "0") == 0 || strcasecmp(par, "no") == 0 || strcasecmp(par, "off") == 0 || strcasecmp(par, "n") == 0) { *resultp = 0; return 1; } else { return 0; } } void *readconfigline(struct configfile *cf) { char *bufp = cf->buf; int buflen = sizeof(cf->buf); //int llen; cf->linenum = cf->linenum_i; for (;;) { char *p = fgets(bufp, buflen, cf->fp); bufp[buflen - 1] = 0; /* Trunc, just in case.. */ if (p == NULL) { if (bufp == cf->buf) return NULL; // EOF! return cf->buf; // Got EOF, but got also data before it! } cf->linenum_i += 1; // Line ending LF ? p = strchr(bufp, '\n'); if (p != NULL) { *p-- = 0; // Possible preceding CR ? if (*p == '\r') *p-- = 0; // Line ending whitespaces ? while (p > bufp && (*p == ' '||*p == '\t')) *p-- = 0; //llen = p - bufp; } if (p == NULL) { p = bufp + strlen(bufp); } if (*p == '\\') { bufp = p; buflen = sizeof(cf->buf) - (p - cf->buf) -1; continue; } else { // Not lone \ at line end. Not a line with continuation line.. break; } } if (debug > 2) printf("Config line: '%s'\n",cf->buf); return cf->buf; } int configline_is_comment(struct configfile *cf) { const char *buf = cf->buf; const int buflen = sizeof(cf->buf); char c = 0; int i; for (i = 0; buf[i] != 0 && i < buflen; ++i) { c = buf[i]; if (c == ' ' || c == '\t') continue; /* Anything else, stop scanning */ break; } if (c == '#' || c == '\n' || c == '\r' || c == 0) return 1; return 0; } int readconfig(const char *name) { struct configfile cf; int has_fault = 0; int i; cf.linenum_i = 1; cf.linenum = 1; cf.name = name; if ((cf.fp = fopen(name, "r")) == NULL) { int e = errno; printf("ERROR: Can not open named config file: '%s' -> %d %s\n", name, e, strerror(e)); return 1; } while (readconfigline(&cf) != NULL) { if (configline_is_comment(&cf)) continue; /* Comment line, or empty line */ i = cfgparam(&cf); if (i) has_fault = 1; } fclose(cf.fp); return has_fault; } aprx-2.08.svn593/ROADMAP0000644000175000017500000000122012016465527013466 0ustar colincolin Aprx Roadmap and Future Directions Version 1 - APRS Rx-only iGate - Complete, working - channel activity monitoring and telemetry - Complete, working Version 2 - Digipeater - Working - Analyze and detect station distance - Working - Radio beacons - Working - Bidirection (Rx/Tx) APRS iGate - Working - DPRS->APRS GW - Working Version 2+ - Port to ucLinux - Planned (pthread OK) - Port to Windows - Automated coverage statistics analyzer, and reporting it via digi node identity beacons. "ALOHA circles" - Automated coverage plotting aprx-2.08.svn593/man-to-html.sh0000644000175000017500000000630012005774517015155 0ustar colincolin#! /bin/sh ## man-page to HTML format converter, when existing ones ## were seriously unacceptable form... ## ## By Matti Aarnio, OH2MQK, , about 1995 unset LC_CTYPE LANG=en_US export LANG TERM=xterm export TERM COLUMNS=80 export COLUMNS LINES=9999 export LINES echo "" basename "$1" echo '