mirror of https://github.com/wb2osz/direwolf.git
683 lines
20 KiB
C
683 lines
20 KiB
C
//
|
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
|
//
|
|
// Copyright (C) 2023 John Langner, WB2OSZ
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* File: deviceid.c
|
|
*
|
|
* Purpose: Determine the device identifier from the destination field,
|
|
* or from prefix/suffix for MIC-E format.
|
|
*
|
|
* Description: Orginally this used the tocalls.txt file and was part of decode_aprs.c.
|
|
* For release 1.8, we use tocalls.yaml and this is split into a separate file.
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
//#define TEST 1 // Standalone test. $ gcc -DTEST deviceid.c && ./a.out
|
|
|
|
|
|
#if TEST
|
|
#define HAVE_STRLCPY 1 // prevent defining in direwolf.h
|
|
#define HAVE_STRLCAT 1
|
|
#endif
|
|
|
|
#include "direwolf.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "deviceid.h"
|
|
#include "textcolor.h"
|
|
|
|
|
|
static void unquote (int line, char *pin, char *pout);
|
|
static int tocall_cmp (const void *px, const void *py);
|
|
static int mice_cmp (const void *px, const void *py);
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Function: main
|
|
*
|
|
* Purpose: A little self-test used during development.
|
|
*
|
|
* Description: Read the yaml file. Decipher a few typical values.
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
#if TEST
|
|
// So we don't need to link with any other files.
|
|
#define dw_printf printf
|
|
void text_color_set(dw_color_t) { return; }
|
|
void strlcpy(char *dst, char *src, size_t dlen) {
|
|
strcpy (dst, src);
|
|
}
|
|
void strlcat(char *dst, char *src, size_t dlen) {
|
|
strcat (dst, src);
|
|
}
|
|
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
char device[80];
|
|
char comment_out[80];
|
|
|
|
deviceid_init ();
|
|
|
|
dw_printf ("\n");
|
|
dw_printf ("Testing ...\n");
|
|
|
|
// MIC-E Legacy (really Kenwood).
|
|
|
|
deviceid_decode_mice (">Comment", comment_out, sizeof(comment_out), device, sizeof(device));
|
|
dw_printf ("%s %s\n", comment_out, device);
|
|
assert (strcmp(comment_out, "Comment") == 0);
|
|
assert (strcmp(device, "Kenwood TH-D7A") == 0);
|
|
|
|
deviceid_decode_mice (">Comment^", comment_out, sizeof(comment_out), device, sizeof(device));
|
|
dw_printf ("%s %s\n", comment_out, device);
|
|
assert (strcmp(comment_out, "Comment") == 0);
|
|
assert (strcmp(device, "Kenwood TH-D74") == 0);
|
|
|
|
deviceid_decode_mice ("]Comment", comment_out, sizeof(comment_out), device, sizeof(device));
|
|
dw_printf ("%s %s\n", comment_out, device);
|
|
assert (strcmp(comment_out, "Comment") == 0);
|
|
assert (strcmp(device, "Kenwood TM-D700") == 0);
|
|
|
|
deviceid_decode_mice ("]Comment=", comment_out, sizeof(comment_out), device, sizeof(device));
|
|
dw_printf ("%s %s\n", comment_out, device);
|
|
assert (strcmp(comment_out, "Comment") == 0);
|
|
assert (strcmp(device, "Kenwood TM-D710") == 0);
|
|
|
|
deviceid_decode_mice ("]\"4V}=", comment_out, sizeof(comment_out), device, sizeof(device));
|
|
dw_printf ("%s %s\n", comment_out, device);
|
|
assert (strcmp(comment_out, "\"4V}") == 0);
|
|
assert (strcmp(device, "Kenwood TM-D710") == 0);
|
|
|
|
|
|
// Modern MIC-E.
|
|
|
|
deviceid_decode_mice ("`Comment_\"", comment_out, sizeof(comment_out), device, sizeof(device));
|
|
dw_printf ("%s %s\n", comment_out, device);
|
|
assert (strcmp(comment_out, "Comment") == 0);
|
|
assert (strcmp(device, "Yaesu FTM-350") == 0);
|
|
|
|
deviceid_decode_mice ("`Comment_ ", comment_out, sizeof(comment_out), device, sizeof(device));
|
|
dw_printf ("%s %s\n", comment_out, device);
|
|
assert (strcmp(comment_out, "Comment") == 0);
|
|
assert (strcmp(device, "Yaesu VX-8") == 0);
|
|
|
|
deviceid_decode_mice ("'Comment|3", comment_out, sizeof(comment_out), device, sizeof(device));
|
|
dw_printf ("%s %s\n", comment_out, device);
|
|
assert (strcmp(comment_out, "Comment") == 0);
|
|
assert (strcmp(device, "Byonics TinyTrak3") == 0);
|
|
|
|
deviceid_decode_mice ("Comment", comment_out, sizeof(comment_out), device, sizeof(device));
|
|
dw_printf ("%s %s\n", comment_out, device);
|
|
assert (strcmp(comment_out, "Comment") == 0);
|
|
assert (strcmp(device, "UNKNOWN vendor/model") == 0);
|
|
|
|
deviceid_decode_mice ("", comment_out, sizeof(comment_out), device, sizeof(device));
|
|
dw_printf ("%s %s\n", comment_out, device);
|
|
assert (strcmp(comment_out, "") == 0);
|
|
assert (strcmp(device, "UNKNOWN vendor/model") == 0);
|
|
|
|
// Tocall
|
|
|
|
deviceid_decode_dest ("APDW18", device, sizeof(device));
|
|
dw_printf ("%s\n", device);
|
|
assert (strcmp(device, "WB2OSZ DireWolf") == 0);
|
|
|
|
deviceid_decode_dest ("APD123", device, sizeof(device));
|
|
dw_printf ("%s\n", device);
|
|
assert (strcmp(device, "Open Source aprsd") == 0);
|
|
|
|
// null for Vendor.
|
|
deviceid_decode_dest ("APAX", device, sizeof(device));
|
|
dw_printf ("%s\n", device);
|
|
assert (strcmp(device, "AFilterX") == 0);
|
|
|
|
deviceid_decode_dest ("APA123", device, sizeof(device));
|
|
dw_printf ("%s\n", device);
|
|
assert (strcmp(device, "UNKNOWN vendor/model") == 0);
|
|
|
|
dw_printf ("\n");
|
|
dw_printf ("Success!\n");
|
|
|
|
exit (EXIT_SUCCESS);
|
|
}
|
|
|
|
#endif // TEST
|
|
|
|
|
|
|
|
// Structures to hold mapping from encoded form to vendor and model.
|
|
// The .yaml file has two separate sections for MIC-E but they can
|
|
// both be handled as a single more general case.
|
|
|
|
struct mice {
|
|
char prefix[4]; // The legacy form has 1 prefix character.
|
|
// The newer form has none. (more accurately ` or ')
|
|
char suffix[4]; // The legacy form has 0 or 1.
|
|
// The newer form has 2.
|
|
char *vendor;
|
|
char *model;
|
|
};
|
|
|
|
struct tocalls {
|
|
char tocall[8]; // Up to 6 characters. Some may have wildcards at the end.
|
|
// Most often they are trailing "??" or "?" or "???" in one case.
|
|
// Sometimes there is trailing "nnn". Does that imply digits only?
|
|
// Sometimes we see a trailing "*". Is "*" different than "?"?
|
|
// There are a couple bizzare cases like APnnnD which can
|
|
// create an ambigious situation. APMPAD, APRFGD, APY0[125]D.
|
|
// Screw them if they can't follow the rules. I'm not putting in a special case.
|
|
char *vendor;
|
|
char *model;
|
|
};
|
|
|
|
|
|
static struct mice *pmice = NULL; // Pointer to array.
|
|
static int mice_count = 0; // Number of allocated elements.
|
|
static int mice_index = -1; // Current index for filling in.
|
|
|
|
static struct tocalls *ptocalls = NULL; // Pointer to array.
|
|
static int tocalls_count = 0; // Number of allocated elements.
|
|
static int tocalls_index = -1; // Current index for filling in.
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Function: deviceid_init
|
|
*
|
|
* Purpose: Called once at startup to read the tocalls.yaml file which was obtained from
|
|
* https://github.com/aprsorg/aprs-deviceid .
|
|
*
|
|
* Inputs: tocalls.yaml with OS specific directory search list.
|
|
*
|
|
* Outputs: static variables listed above.
|
|
*
|
|
* Description: For maximum flexibility, we will read the
|
|
* data file at run time rather than compiling it in.
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
// Make sure the array is null terminated.
|
|
// If search order is changed, do the same in symbols.c for consistency.
|
|
// fopen is perfectly happy with / in file path when running on Windows.
|
|
|
|
static const char *search_locations[] = {
|
|
(const char *) "tocalls.yaml", // Current working directory
|
|
(const char *) "data/tocalls.yaml", // Windows with CMake
|
|
(const char *) "../data/tocalls.yaml", // Source tree
|
|
#ifndef __WIN32__
|
|
(const char *) "/usr/local/share/direwolf/tocalls.yaml",
|
|
(const char *) "/usr/share/direwolf/tocalls.yaml",
|
|
#endif
|
|
#if __APPLE__
|
|
// https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/2458
|
|
// Adding the /opt/local tree since macports typically installs there. Users might want their
|
|
// INSTALLDIR (see Makefile.macosx) to mirror that. If so, then we need to search the /opt/local
|
|
// path as well.
|
|
(const char *) "/opt/local/share/direwolf/tocalls.yaml",
|
|
#endif
|
|
(const char *) NULL // Important - Indicates end of list.
|
|
};
|
|
|
|
|
|
void deviceid_init(void)
|
|
{
|
|
FILE *fp = NULL;
|
|
for (int n = 0; search_locations[n] != NULL && fp == NULL; n++) {
|
|
#if TEST
|
|
text_color_set(DW_COLOR_INFO);
|
|
dw_printf ("Trying %s\n", search_locations[n]);
|
|
#endif
|
|
fp = fopen(search_locations[n], "r");
|
|
#if TEST
|
|
if (fp != NULL) {
|
|
dw_printf ("Opened %s\n", search_locations[n]);
|
|
}
|
|
#endif
|
|
};
|
|
|
|
if (fp == NULL) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf("Could not open any of these file locations:\n");
|
|
for (int n = 0; search_locations[n] != NULL; n++) {
|
|
dw_printf (" %s\n", search_locations[n]);
|
|
}
|
|
dw_printf("It won't be possible to extract device identifiers from packets.\n");
|
|
return;
|
|
};
|
|
|
|
// Read file first time to get number of items.
|
|
// Allocate required space.
|
|
// Rewind.
|
|
// Read file second time to gather data.
|
|
|
|
enum { no_section, mice_section, tocalls_section} section = no_section;
|
|
char stuff[80];
|
|
|
|
for (int pass = 1; pass <=2; pass++) {
|
|
int line = 0; // Line number within file.
|
|
|
|
while (fgets(stuff, sizeof(stuff), fp)) {
|
|
line++;
|
|
|
|
// Remove trailing CR/LF or spaces.
|
|
char *p = stuff + strlen(stuff) - 1;
|
|
while (p >= (char*)stuff && (*p == '\r' || *p == '\n' || *p == ' ')) {
|
|
*p-- = '\0';
|
|
}
|
|
|
|
// Ignore comment lines.
|
|
if (stuff[0] == '#') {
|
|
continue;
|
|
}
|
|
|
|
#if TEST
|
|
//dw_printf ("%d: %s\n", line, stuff);
|
|
#endif
|
|
// This is not very robust; everything better be in exactly the right format.
|
|
|
|
if (strncmp(stuff, "mice:", strlen("mice:")) == 0) {
|
|
section = mice_section;
|
|
#if TEST
|
|
dw_printf ("Pass %d, line %d, MIC-E section\n", pass, line);
|
|
#endif
|
|
}
|
|
else if (strncmp(stuff, "micelegacy:", strlen("micelegacy:")) == 0) {
|
|
section = mice_section; // treat both same.
|
|
#if TEST
|
|
dw_printf ("Pass %d, line %d, Legacy MIC-E section\n", pass, line);
|
|
#endif
|
|
}
|
|
else if (strncmp(stuff, "tocalls:", strlen("tocalls:")) == 0) {
|
|
section = tocalls_section;
|
|
#if TEST
|
|
dw_printf ("Pass %d, line %d, TOCALLS section\n", pass, line);
|
|
#endif
|
|
}
|
|
|
|
// The first property of an item is preceded by " - ".
|
|
|
|
if (pass == 1 && strncmp(stuff, " - ", 3) == 0) {
|
|
switch (section) {
|
|
case no_section: break;
|
|
case mice_section: mice_count++; break;
|
|
case tocalls_section: tocalls_count++; break;
|
|
}
|
|
}
|
|
|
|
if (pass == 2) {
|
|
switch (section) {
|
|
case no_section:
|
|
break;
|
|
|
|
case mice_section:
|
|
if (strncmp(stuff, " - ", 3) == 0) {
|
|
mice_index++;
|
|
assert (mice_index >= 0 && mice_index < mice_count);
|
|
}
|
|
if (strncmp(stuff+3, "prefix: ", strlen("prefix: ")) == 0) {
|
|
unquote (line, stuff+3+8, pmice[mice_index].prefix);
|
|
}
|
|
else if (strncmp(stuff+3, "suffix: ", strlen("suffix: ")) == 0) {
|
|
unquote (line, stuff+3+8, pmice[mice_index].suffix);
|
|
}
|
|
else if (strncmp(stuff+3, "vendor: ", strlen("vendor: ")) == 0) {
|
|
pmice[mice_index].vendor = strdup(stuff+3+8);
|
|
}
|
|
else if (strncmp(stuff+3, "model: ", strlen("model: ")) == 0) {
|
|
pmice[mice_index].model = strdup(stuff+3+7);
|
|
}
|
|
break;
|
|
|
|
case tocalls_section:
|
|
if (strncmp(stuff, " - ", 3) == 0) {
|
|
tocalls_index++;
|
|
assert (tocalls_index >= 0 && tocalls_index < tocalls_count);
|
|
}
|
|
if (strncmp(stuff+3, "tocall: ", strlen("tocall: ")) == 0) {
|
|
// Remove trailing wildcard characters ? * n
|
|
char *r = stuff + strlen(stuff) - 1;
|
|
while (r >= (char*)stuff && (*r == '?' || *r == '*' || *r == 'n')) {
|
|
*r-- = '\0';
|
|
}
|
|
|
|
strlcpy (ptocalls[tocalls_index].tocall, stuff+3+8, sizeof(ptocalls[tocalls_index].tocall));
|
|
|
|
// Remove trailing CR/LF or spaces.
|
|
char *p = stuff + strlen(stuff) - 1;
|
|
while (p >= (char*)stuff && (*p == '\r' || *p == '\n' || *p == ' ')) {
|
|
*p-- = '\0';
|
|
}
|
|
}
|
|
else if (strncmp(stuff+3, "vendor: ", strlen("vendor: ")) == 0) {
|
|
ptocalls[tocalls_index].vendor = strdup(stuff+3+8);
|
|
}
|
|
else if (strncmp(stuff+3, "model: ", strlen("model: ")) == 0) {
|
|
ptocalls[tocalls_index].model = strdup(stuff+3+7);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} // while(fgets
|
|
|
|
if (pass == 1) {
|
|
#if TEST
|
|
dw_printf ("deviceid sizes %d %d\n", mice_count, tocalls_count);
|
|
#endif
|
|
pmice = calloc(sizeof(struct mice), mice_count);
|
|
ptocalls = calloc(sizeof(struct tocalls), tocalls_count);
|
|
|
|
rewind (fp);
|
|
section = no_section;
|
|
}
|
|
} // for pass = 1 or 2
|
|
|
|
fclose (fp);
|
|
|
|
assert (mice_index == mice_count - 1);
|
|
assert (tocalls_index == tocalls_count - 1);
|
|
|
|
|
|
// MIC-E Legacy needs to be sorted so those with suffix come first.
|
|
|
|
qsort (pmice, mice_count, sizeof(struct mice), mice_cmp);
|
|
|
|
|
|
// Sort tocalls by decreasing length so the search will go from most specific to least specific.
|
|
// Example: APY350 or APY008 would match those specific models before getting to the more generic APY.
|
|
|
|
qsort (ptocalls, tocalls_count, sizeof(struct tocalls), tocall_cmp);
|
|
|
|
|
|
#if TEST
|
|
dw_printf ("MIC-E:\n");
|
|
for (int i = 0; i < mice_count; i++) {
|
|
dw_printf ("%s %s %s\n", pmice[i].suffix, pmice[i].vendor, pmice[i].model);
|
|
}
|
|
dw_printf ("TOCALLS:\n");
|
|
for (int i = 0; i < tocalls_count; i++) {
|
|
dw_printf ("%s %s %s\n", ptocalls[i].tocall, ptocalls[i].vendor, ptocalls[i].model);
|
|
}
|
|
#endif
|
|
|
|
return;
|
|
|
|
} // end deviceid_init
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Function: unquote
|
|
*
|
|
* Purpose: Remove surrounding quotes and undo any escapes.
|
|
*
|
|
* Inputs: line - File line number for error message.
|
|
*
|
|
* in - String with quotes. Might contain \ escapes.
|
|
*
|
|
* Outputs: out - Quotes and escapes removed.
|
|
* Limited to 2 characters to avoid buffer overflow.
|
|
*
|
|
* Examples: in out
|
|
* "_#" _#
|
|
* "_\"" _"
|
|
* "=" =
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
static void unquote (int line, char *pin, char *pout)
|
|
{
|
|
int count = 0;
|
|
|
|
*pout = '\0';
|
|
if (*pin != '"') {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf("Missing leading \" for %s on line %d.\n", pin, line);
|
|
return;
|
|
}
|
|
|
|
pin++;
|
|
while (*pin != '\0' && *pin != '\"' && count < 2) {
|
|
if (*pin == '\\') {
|
|
pin++;
|
|
}
|
|
*pout++ = *pin++;
|
|
count++;
|
|
}
|
|
*pout = '\0';
|
|
|
|
if (*pin != '"') {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf("Missing trailing \" or string too long on line %d.\n", line);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Used to sort the tocalls by length.
|
|
// When length is equal, alphabetically.
|
|
|
|
static int tocall_cmp (const void *px, const void *py)
|
|
{
|
|
const struct tocalls *x = (struct tocalls *)px;
|
|
const struct tocalls *y = (struct tocalls *)py;
|
|
|
|
if (strlen(x->tocall) != strlen(y->tocall)) {
|
|
return (strlen(y->tocall) - strlen(x->tocall));
|
|
}
|
|
return (strcmp(x->tocall, y->tocall));
|
|
}
|
|
|
|
// Used to sort the suffixes by length.
|
|
// Longer at the top.
|
|
// Example check for >xxx^ before >xxx .
|
|
|
|
static int mice_cmp (const void *px, const void *py)
|
|
{
|
|
const struct mice *x = (struct mice *)px;
|
|
const struct mice *y = (struct mice *)py;
|
|
|
|
return (strlen(y->suffix) - strlen(x->suffix));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Function: deviceid_decode_dest
|
|
*
|
|
* Purpose: Find vendor/model for destination address of form APxxxx.
|
|
*
|
|
* Inputs: dest - Destination address. No SSID.
|
|
*
|
|
* device_size - Amount of space available for result to avoid buffer overflow.
|
|
*
|
|
* Outputs: device - Vendor and model.
|
|
*
|
|
* Description: With the exception of MIC-E format, we expect to find the vendor/model in the
|
|
* AX.25 destination field. The form should be APxxxx.
|
|
*
|
|
* Search the list looking for the maximum length match.
|
|
* For example,
|
|
* APXR = Xrouter
|
|
* APX = Xastir
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
void deviceid_decode_dest (char *dest, char *device, size_t device_size)
|
|
{
|
|
strlcpy (device, "UNKNOWN vendor/model", device_size);
|
|
|
|
if (ptocalls == NULL) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf("deviceid_decode_dest called without any deviceid data.\n");
|
|
return;
|
|
}
|
|
|
|
for (int n = 0; n < tocalls_count; n++) {
|
|
if (strncmp(dest, ptocalls[n].tocall, strlen(ptocalls[n].tocall)) == 0) {
|
|
|
|
if (ptocalls[n].vendor != NULL) {
|
|
strlcpy (device, ptocalls[n].vendor, device_size);
|
|
}
|
|
|
|
if (ptocalls[n].vendor != NULL && ptocalls[n].model != NULL) {
|
|
strlcat (device, " ", device_size);
|
|
}
|
|
|
|
if (ptocalls[n].model != NULL) {
|
|
strlcat (device, ptocalls[n].model, device_size);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Not found in table.
|
|
strlcpy (device, "UNKNOWN vendor/model", device_size);
|
|
|
|
} // end deviceid_decode_dest
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Function: deviceid_decode_mice
|
|
*
|
|
* Purpose: Find vendor/model for MIC-E comment.
|
|
*
|
|
* Inputs: comment - MIC-E comment that might have vendor/model encoded as
|
|
* a prefix and/or suffix.
|
|
* Any trailing CR has already been removed.
|
|
*
|
|
* trimmed_size - Amount of space available for result to avoid buffer overflow.
|
|
*
|
|
* device_size - Amount of space available for result to avoid buffer overflow.
|
|
*
|
|
* Outputs: trimmed - Final comment with device vendor/model removed.
|
|
* This would include any altitude.
|
|
*
|
|
* device - Vendor and model.
|
|
*
|
|
* Description: MIC-E device identification has a tortured history.
|
|
*
|
|
* The Kenwood TH-D7A put ">" at the beginning of the comment.
|
|
* The Kenwood TM-D700 put "]" at the beginning of the comment.
|
|
* Later Kenwood models also added a single suffix character
|
|
* using a character very unlikely to appear at the end of a comment.
|
|
*
|
|
* The later convention, used by everyone else, is to have a prefix of ` or '
|
|
* and a suffix of two characters. The suffix characters need to be
|
|
* something very unlikely to be found at the end of a comment.
|
|
*
|
|
* A receiving device is expected to remove those extra characters
|
|
* before displaying the comment.
|
|
*
|
|
* References: http://www.aprs.org/aprs12/mic-e-types.txt
|
|
* http://www.aprs.org/aprs12/mic-e-examples.txt
|
|
* https://github.com/wb2osz/aprsspec containing:
|
|
* APRS Protocol Specification 1.2
|
|
* Understanding APRS Packets
|
|
*------------------------------------------------------------------*/
|
|
|
|
// The strncmp documentation doesn't mention behavior if length is zero.
|
|
// Do our own just to be safe.
|
|
|
|
static inline int strncmp_z (char *a, char *b, size_t len)
|
|
{
|
|
int result = 0;
|
|
if (len > 0) {
|
|
result = strncmp(a, b, len);
|
|
}
|
|
//dw_printf ("Comparing '%s' and '%s' len %d result %d\n", a, b, len, result);
|
|
return result;
|
|
}
|
|
|
|
void deviceid_decode_mice (char *comment, char *trimmed, size_t trimmed_size, char *device, size_t device_size)
|
|
{
|
|
strlcpy (device, "UNKNOWN vendor/model", device_size);
|
|
strlcpy (trimmed, comment, trimmed_size);
|
|
if (strlen(comment) < 1) {
|
|
return;
|
|
}
|
|
|
|
if (ptocalls == NULL) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf("deviceid_decode_mice called without any deviceid data.\n");
|
|
return;
|
|
}
|
|
|
|
|
|
// The Legacy format has an explicit prefix in the table.
|
|
// For others, it must be ` or ' to indicate whether messaging capable.
|
|
|
|
for (int n = 0; n < mice_count; n++) {
|
|
if ((strlen(pmice[n].prefix) != 0 && // Legacy
|
|
strncmp_z(comment, // prefix from table
|
|
pmice[n].prefix,
|
|
strlen(pmice[n].prefix)) == 0 &&
|
|
strncmp_z(comment + strlen(comment) - strlen(pmice[n].suffix), // possible suffix
|
|
pmice[n].suffix,
|
|
strlen(pmice[n].suffix)) == 0) ||
|
|
|
|
(strlen(pmice[n].prefix) == 0 && // Later
|
|
(comment[0] == '`' || comment[0] == '\'') && // prefix ` or '
|
|
strncmp_z(comment + strlen(comment) - strlen(pmice[n].suffix), // suffix
|
|
pmice[n].suffix,
|
|
strlen(pmice[n].suffix)) == 0) ) {
|
|
|
|
if (pmice[n].vendor != NULL) {
|
|
strlcpy (device, pmice[n].vendor, device_size);
|
|
}
|
|
|
|
if (pmice[n].vendor != NULL && pmice[n].model != NULL) {
|
|
strlcat (device, " ", device_size);
|
|
}
|
|
|
|
if (pmice[n].model != NULL) {
|
|
strlcat (device, pmice[n].model, device_size);
|
|
}
|
|
|
|
// Remove any prefix/suffix and return what remains.
|
|
|
|
strlcpy (trimmed, comment + 1, trimmed_size);
|
|
trimmed[strlen(comment) - 1 - strlen(pmice[n].suffix)] = '\0';
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// Not found.
|
|
|
|
strlcpy (device, "UNKNOWN vendor/model", device_size);
|
|
strlcpy (trimmed, comment, trimmed_size);
|
|
|
|
} // end deviceid_decode_mice
|
|
|
|
// end deviceid.c
|