/*************************************************************************************** * Filename: sortlog.c * Author: Richard Everitt G4ZFE * g4zfe@g4zfe.com * Version: $Header: /home/rhe/tools/RCS/sortlog.c,v 1.27 2006/03/05 11:50:25 rhe Exp $ * Purpose: Program to sort a contest log into alphabetical order * and then split into txt files for use by the Log * Search Java applet * Input: Printer output file (.prt), ascii log (.res or .log), * ADIF (Amateur Radio Data Interchange Format) or Cabrillo * Changes: * 15/JUN/97: Added ADIF support * 12/DEC/97: Added nodate, notime flags * 31/DEC/97: SORTing fixed. * 04/MAR/98: ADIF support improved * 03/SEP/98: Extract ADIF "Comment" field * 11/OCT/98: Number of QSOs changed from int to long * Added support for SAT QSOs * 13/FEB/99: Added support for ADIF * Cache date for ADIF logs that do not have * date in each record. * 02/SEP/00: Update ADIF support * Just needed for ADIF detection * 11/MAR/01: ADIF fixes from DF4XX (many thanks!!) * 22/SEP/01: ADIF fixes from BA4RF (many thanks!!) * Added support for cabrillo format * 26/OCT/01: ADIF support fixed * 13/NOV/01: Added -lower switch to create lower case filenames * Fixed sorting of output files (dependant on callsign position) * Added more Cabrillo log formats * ADIF fix for EA6VQ * Fixed overflow error with long (> 80 chars) ADIF lines * 11/JAN/02: Fixed error when fields parsed as callsign start with non-alnum * 23/JAN/02: ADIF: if both BAND and FREQ data available then use BAND * 26/JAN/02: ADIF: specify ADIF tags exactly to prevent conflict with text * in comments field * ADIF: read COMMENTS field correctly * 16/JUN/02: Added code fix from AD1C for callsigns such as LX/DF0AR * Added PSK31 as a known mode. PSK31 is truncated to PSK for * display * Fixed problem with very long ADIF lines (>255 chars) * 16/NOV/02: Added MFSK16, HELL and TOR as known modes. * MFSK16 is truncated to MFSK for display * 19/DEC/02: Fixed problems decoding frequency in XMLog .txt files * 22/JAN/03: Fixed problem with very very long ADIF lines (>512 chars) * 06/JUL/03: Added support for PSK63/WSJT/FSK441/JT6M/JT44. All modes are * truncated to 2 characters. Truncated RTTY to RY. * Added support for the new 5 MHz/60m band * 20/MAR/04: Default to using nodate and notime * 22/MAY/04: ADIF: if both BAND and FREQ data available then use BAND * 19/SEP/04: Added BPSK63 as a known mode * 01/JAN/05: Added Kids Roundup to list of known Cabrillo contests * 12/APR/05: BUGFIX: Frequency 3.6 was been truncated to 3. in the output file * 05/MAR/06: Rationalisation of ADIF modes to CW/PHO/IMG/DAT as per LoTW. ****************************************************************************************/ #include #include #include #include #include #include #include #define NUMB_COMMON_PREFIXES 13 #define TRUE 1 #define FALSE 0 /* Cabrillo contest types */ #define CBR_DEFAULT 0 #define CBR_IOTA 1 #define CBR_ARRLVHF 2 #define CBR_CQWWRTTY 3 #define CBR_SWEEPSTAKES 4 #define CBR_IARU 5 #define CBR_APSPRINT 6 #define CBR_ARRL10 7 #define CBR_ARRL160 8 #define CBR_ARRLDX 9 #define CBR_CQWPX 10 #define CBR_RSGB2128 11 #define CBR_KIDS 12 #define SPACE_CHAR 32 #define MAX_FREQ_LENGTH 6 /* truncate ADIF frequencies to 5 characters */ /* Columns to write out; */ int column = 0; int date_pos = -1; int time_pos = -1; int band_pos = -1; int mode_pos = -1; int call_pos = -1; /* Streams to the most common callsigns. The files containing the most */ /* common callsigns are left open to attempt to improve performance */ /* This is needed as I was unable to get my DOS compiler to allow more */ /* than 20 files to be opened at once! Tell me how to do this! */ FILE *temp_files[NUMB_COMMON_PREFIXES]; int ADIF_format = FALSE; int Cabrillo_format = FALSE; int no_date_flag = TRUE; int no_time_flag = TRUE; int comment_flag = FALSE; int lower_case_filenames_flag = FALSE; int callsign_column = 0; int cabrillo_contest = CBR_DEFAULT; /* Which Cabrillo format the log is in */ void close_temp_file () { int i; for (i=0; i < NUMB_COMMON_PREFIXES; i++) fclose (temp_files[i]); } void convert_mode (const char *input_mode, char *output_mode) { /* Set mode - CW/PHO/IMG/DAT */ /* CW mode */ if (! strcmp (input_mode,"CW") || ! strcmp (input_mode,"A1A")) { strcpy (output_mode,"CW"); return; } /* PHONE modes */ if (! strcmp (input_mode,"PHONE") || ! strcmp (input_mode,"AM") || !strcmp (input_mode,"FM") || !strcmp (input_mode,"SSB") || !strcmp (input_mode,"LSB") || !strcmp (input_mode,"USB") || !strcmp (input_mode,"J3E") || !strcmp (input_mode,"F3E") || !strcmp (input_mode,"A3E")) { strcpy (output_mode,"PHO"); return; } /* IMAGE modes */ if (! strcmp (input_mode,"ATV") || ! strcmp (input_mode,"FAX") || !strcmp (input_mode,"SSTV") || !strcmp (input_mode,"IMAGE")) { strcpy (output_mode,"IMG"); return; } /* DATA modes */ if (! strcmp (input_mode,"AMTOR") || ! strcmp (input_mode,"CLOVER") || !strcmp (input_mode,"FSK31") || !strcmp (input_mode,"FSK441") || ! strcmp (input_mode,"GTOR") || ! strcmp (input_mode,"HELL") || !strcmp (input_mode,"HFSK") || !strcmp (input_mode,"JT65") || ! strcmp (input_mode,"MFSK16") || ! strcmp (input_mode,"MFSK8") || !strcmp (input_mode,"MINIRTTY") || !strcmp (input_mode,"MT63") || ! strcmp (input_mode,"OLIVIA") || ! strcmp (input_mode,"PACKET") || !strcmp (input_mode,"PACTOR") || !strcmp (input_mode,"PSK125") || ! strcmp (input_mode,"PSK31") || ! strcmp (input_mode,"PSK63") || !strcmp (input_mode,"Q15") || !strcmp (input_mode,"RTTY") || ! strcmp (input_mode,"THROB") || ! strcmp (input_mode,"TOR") || !strcmp (input_mode,"RY") || !strcmp (input_mode,"DATA")) { strcpy (output_mode,"DAT"); return; } /* Unknown mode */ strcpy (output_mode,"DAT"); } void open_temp_file (char first_char, FILE *out) { if (lower_case_filenames_flag == TRUE) first_char = tolower(first_char); switch (first_char) { case 'K': case 'k': out = temp_files[0]; break; case 'O': case 'o': out = temp_files[1]; break; case 'W': case 'w': out = temp_files[2]; break; case 'D': case 'd': out = temp_files[3]; break; case 'U': case 'u': out = temp_files[4]; break; case 'S': case 's': out = temp_files[5]; break; case 'N': case 'n': out = temp_files[6]; break; case 'R': case 'r': out = temp_files[7]; break; case 'H': case 'h': out = temp_files[8]; break; case 'Y': case 'y': out = temp_files[9]; break; case 'I': case 'i': out = temp_files[10]; break; case 'L': case 'l': out = temp_files[11]; break; case 'G': case 'g': out = temp_files[12]; break; default: out = NULL; break; } } void write_temp_file (char first_char, char *line) { FILE *out = NULL; char tmp_fname [6]; /* Write a line to the temporary file. If the callsign is a */ /* "common" callsign then the file does not need to be opened. */ /* Otherwise open the file and write the line and then close. */ /* Ignore invalid characters */ if (!isalnum(first_char)) return; open_temp_file (first_char, out); if (out == NULL) { if (lower_case_filenames_flag == TRUE) sprintf (tmp_fname,"%c.tmp",tolower(first_char)); else sprintf (tmp_fname,"%c.TMP",first_char); out = fopen (tmp_fname, "a"); fprintf (out, line); fclose (out); } else fprintf (out, line); } int check_band (char *p) { int z; /* band must be between 1 and 7 characters long */ if (strlen(p) < 1 || strlen(p) > 7) return (FALSE); z = strlen(p); /* band must have '. , :' if more than 4 characters */ if (strlen(p) > 4) { if (strchr (p,'.') == NULL && strchr (p,',') == NULL && strchr (p,':') == NULL ) return (FALSE); } if ( strstr (p,"1.") != NULL || strstr (p,"3.") != NULL || strstr (p,"5.") != NULL || strstr (p,"7") != NULL || strstr (p,"14") != NULL || strstr (p,"18") != NULL || strstr (p,"21") != NULL || strstr (p,"24") != NULL || strstr (p,"28") != NULL || strstr (p,"160") != NULL || strstr (p,"75") != NULL || strstr (p,"80") != NULL || strstr (p,"60") != NULL || strstr (p,"40") != NULL || strstr (p,"30") != NULL || strstr (p,"20") != NULL || strstr (p,"17") != NULL || strstr (p,"12") != NULL || strstr (p,"15") != NULL || strstr (p,"50") != NULL || strstr (p,"70") != NULL || strstr (p,"144") != NULL || strstr (p,"430") != NULL || strstr (p,"SAT") != NULL || strstr (p,"10") != NULL) return (TRUE); else return (FALSE); } int check_mode (char *p) { /* Mode must be between 2 and 5 chars long */ if (strlen(p) < 2 || strlen(p) >5) return (FALSE); if (! strcmp (p,"SSB") || !strcmp (p,"J3E") || !strcmp (p,"PHO") || !strcmp (p,"SSB") || !strcmp (p,"IMAGE") || !strcmp (p,"DATA") || !strcmp (p,"RTTY") || !strcmp (p,"SSTV") || !strcmp (p,"F1B") || !strcmp (p,"FSK") || !strcmp (p,"AM") || !strcmp (p,"CW") || !strcmp (p,"A1A") || !strcmp (p,"FM") || !strcmp (p,"AMTOR") || !strcmp (p,"PACKET") || !strcmp (p,"TOR") || !strcmp (p,"ATV") || !strcmp (p,"FAX") || !strcmp (p,"MFSK8") || !strcmp (p,"MFSK16") || !strcmp (p,"CLOVER") || !strcmp (p,"OLIVIA") || !strcmp (p,"HFSK") || !strcmp (p,"MFSK") || !strcmp (p,"HELL") || !strcmp (p,"THROB") || !strcmp (p,"PSK") || !strcmp (p,"PSK63") || !strcmp (p,"BPSK63") || !strcmp (p,"WSJT") || !strcmp (p,"JT6M") || !strcmp (p,"JT6M") || !strcmp (p,"JT65") || !strcmp (p,"MT63") || !strcmp (p,"JT44") || !strcmp (p,"Q15") || !strcmp (p,"FSK31") || !strcmp (p,"FSK441") || !strcmp (p,"PSK125") || !strcmp (p,"PSK63") || !strcmp (p,"PSK31")) return (TRUE); else return (FALSE); } int check_call (char *p) { int i; int numeric_count = 0; int alpha_count = 0; /* Callsign must be at least 3 characters long */ if (strlen(p) < 3) return (FALSE); /* Callsign is only alpha-numerics and "/" chars */ for (i=0; i < (int) strlen(p); i++) { if (isalnum (p[i]) || strchr(p,'/')) { /* valid character for callsign */ } else return (FALSE); } /* Check that the callsign contains at least one */ /* numeric and alpha */ for (i=0; i < (int) strlen(p); i++) { if (isdigit (p[i])) numeric_count++; if (isalpha (p[i])) alpha_count++; } if (numeric_count == 0 || alpha_count == 0) return (FALSE); numeric_count=0; alpha_count=0; /* Ignore text (i.e > 4 characters) as callsigns */ for (i=0; i < (int) strlen(p); i++) { if (isdigit (p[i])) break; if (isalpha (p[i])) alpha_count++; /* Break on slash character to allow for callsigns */ /* such as LX/DF0AR */ if (p[i] == '/') break; } if (alpha_count > 3) return (FALSE); return (TRUE); } void parse_line (char *p) { int i; /* Is this the date? */ if (date_pos == -1 && strlen(p) >= 6 ) { if (strlen(p) == 6 || (strchr (p,'-') || strchr (p,'/'))) { date_pos = column; fprintf(stderr,"Using column %d \(%s\) as date\n", date_pos, p); return; } } /* Is this the time? */ if (strlen(p) >= 4 && time_pos == -1) { i = 0; while (i < (int) strlen(p) && isdigit (p[i])) i++; if (i == (int) strlen(p)) { time_pos = column; fprintf (stderr,"Using column %d \(%s\) as time\n", time_pos, p); return; } } /* Is this the band? */ if (band_pos == -1) { if (check_band (p)) { band_pos = column; fprintf (stderr,"Using column %d \(%s\) as band\n", band_pos, p); return; } } /* Is this the mode? */ if (mode_pos == -1) { if (check_mode (p)) { mode_pos = column; fprintf (stderr,"Using column %d \(%s\) as mode\n", mode_pos, p); return; } } /* Is this the callsign? */ if (call_pos == -1) { if (check_call (p)) { call_pos = column; fprintf (stderr,"Using column %d \(%s\) as callsign\n", call_pos, p); return; } } } int check_valid_line (char ipline[]) { int i; char *p; int call_ok = FALSE; char local_ipline[128]; if (strlen(ipline) > 1) { ipline[strlen(ipline)-1] = '\0'; } else { /* Empty line */ return (FALSE); } /* Convert to upper case */ for (i=0; i <= (int) strlen(ipline); i++) local_ipline[i] = toupper (ipline[i]); /* Check if ADIF format */ if (strstr (local_ipline, "ADIF") != NULL || strstr (local_ipline,"") != NULL || strstr (local_ipline,"EA6VQ") != NULL) { ADIF_format = TRUE; return (TRUE); } /* Check if cabrillo format */ if (strstr (local_ipline, "START-OF-LOG:") != NULL ) { Cabrillo_format = TRUE; return (TRUE); } /* Ignore lines with Line Feeds in */ if (strchr(local_ipline,12)) /* LF */ return (FALSE); /* Ignore lines with "PAGE" in */ if (strstr (local_ipline, "PAGE") != NULL) return (FALSE); /* Ignore lines with "CALL" in */ if (strstr (local_ipline, "CALL") != NULL) return (FALSE); /* Ignore lines with "WINLOG" in */ if (strstr (local_ipline, "WINLOG") != NULL) return (FALSE); /* Ignore lines with "LOGNAME" in */ if (strstr (local_ipline, "LOGNAME") != NULL) return (FALSE); /* Check that the line contains a callsign */ p = strtok (local_ipline, " "); call_ok = check_call (p); if (call_ok) return (TRUE); while ( (p = strtok (NULL, " ")) != NULL) { call_ok = check_call (p); if (call_ok) return (TRUE); } return (FALSE); } void process_ADIF_format (FILE* in, char *iline) { long qso_count = 0; char ipline [1024]; char line[1024]; char opline [1024]; int start = FALSE; int end = FALSE; int comment_found = FALSE; int band_or_freq_found = FALSE; /* Process band or freq whichever is first, not both! */ char call[512]; char date[512]; char previous_date[512]; /* XMLOG doesn't write date to every record */ char time[512]; char band[512]; char mode[512]; char comment[512]; int length; int i; int values; char dummy; char *p; if (strlen(iline) > 1) { line[strlen(iline)-1] = '\0'; } /* Convert to upper case */ for (i=0; i <= (int) strlen(iline); i++) ipline[i] = toupper (iline[i]); /* Check if ADIF format */ if (strstr (ipline,"") != NULL) { /* End of Header found - no need to skip */ } else { /* Skip ADIF header info */ while (start == FALSE) { if (fgets (ipline, sizeof(ipline), in) == NULL) return; /* Convert to upper case */ for (i=0; i <= (int) strlen(ipline); i++) line[i] = toupper (ipline[i]); if (strstr (line, "") != NULL) start = TRUE; } } fprintf (stderr,"\nADIF format detected\n"); fprintf (stderr, "\nEach dot is 100 QSOs. Writing logs\n"); /* Read in the rest of the input file */ while (fgets (ipline, sizeof(ipline), in)) { /* Skip blank lines */ while (strlen (ipline) <= 1) if (fgets (ipline, sizeof(ipline), in) == NULL) { fprintf (stderr, "\n\n%d QSOs processed\n", qso_count); return; } /* Remove LF characeters */ ipline [sizeof(ipline) - 1] = '\0'; /* Clear out the previous values */ memset (date, SPACE_CHAR, sizeof(date)); memset (time, SPACE_CHAR, sizeof(time)); memset (band, SPACE_CHAR, sizeof(band)); memset (mode, SPACE_CHAR, sizeof(mode)); memset (call, SPACE_CHAR, sizeof(call)); memset (comment, SPACE_CHAR, sizeof(comment)); /* ADIF record may be on > 1 line */ end = FALSE; comment_found = FALSE; band_or_freq_found = FALSE; while (end == FALSE) { /* Skip blank lines */ while (strlen (ipline) <= 1) if (fgets (ipline, sizeof(ipline), in) == NULL) { return; } /* Convert to upper case */ for (i=0; i <= (int) strlen(ipline); i++) line[i] = toupper (ipline[i]); /* Read following fields: */ /* Call, QSO_Date, Time_On, Mode*/ /* Band. Allow for both */ /* and */ /* formats */ /* Parse QSO details */ p = strstr (line, "\%s ", &length,&call); if (values != 2) sscanf (p, "\%s ", &length,&dummy,&call); call[length] = '\0'; } if (comment_flag == TRUE) /* Read the Comment field */ { if ((p = strstr (line, "\%s", &length,&comment[3]);*/ values = sscanf (p, "\", &length); strncpy (&comment[3], p + 12, length); if (values != 1) { sscanf (p, "\", &length,&dummy); strncpy (&comment[3], p + 15, length); } comment [length+3] = '\0'; } } if (comment_found == FALSE) comment[0] = '\0'; if ((p = strstr (line, "\%s ", &length,&band); if (values != 2) sscanf (p, "\%s ", &length,&dummy,&band); /* Strip the "M" off e.g 40M becomes 40 */ if ((p = strchr (band, 'M')) != NULL) *p = '\0'; /* Strip the "CM" off e.g 70CM becomes 70 */ if ((p = strchr (band, 'C')) != NULL) *p = '\0'; band_or_freq_found = TRUE; } if ((p = strstr (line, "\%s ", &length,&band); if (values != 2) sscanf (p, "\%s ", &length,&dummy,&band); /* Trim frequency */ if (length > MAX_FREQ_LENGTH) band [MAX_FREQ_LENGTH] = '\0'; else band[length] = '\0'; band_or_freq_found = TRUE; } /* Ignore PROP_MODE and other _MODE records */ if ((p = strstr (line, "\%s ", &length,&mode); if (values != 2) sscanf (p, "\%s ", &length,&dummy,&mode); mode[length] = '\0'; /* Convert the mode to one of the following as per LoTW - CW/PHO/IMG/DAT */ convert_mode (&mode, &mode); } if ((p = strstr (line, "\%s ", &length, &date); if (values != 2) sscanf (p, "\%s ", &length, &dummy, &date); date[length] = '\0'; /* Keep track of the date in case the next record doesn't have */ /* the date (as in XMLOG) */ strcpy (previous_date, date); } if ((p = strstr (line, "\%s ",&length, &time); if (values != 2) sscanf (p, "\%s ",&length, &dummy, &time); time[length] = '\0'; /* Ignore seconds part of time */ if (strlen(time) > 4) time[4] = '\0'; } /* Continue reading until End */ /* Of Record */ if (strstr (line, "") != NULL) end = TRUE; else { end = FALSE; fgets (ipline, sizeof(ipline), in); } } qso_count++; /* Display some progress! */ if (qso_count % 100 == 0) putchar ('.'); if (qso_count % 8000 == 0) putchar ('\n'); if (!strcmp (date,"")) { strcpy (date, previous_date); } if (comment_flag == FALSE) { if (no_date_flag == TRUE && no_time_flag == TRUE) { sprintf (opline,"% 8s% 4s %s\n", band, mode, call); callsign_column = 14; } if (no_date_flag == TRUE && no_time_flag == FALSE) { sprintf (opline,"% 4s % 8s% 4s %s\n", time, band, mode, call); callsign_column = 19; } if (no_date_flag == FALSE && no_time_flag == TRUE) { sprintf (opline,"% 9s % 8s% 4s %s\n", date, band, mode, call); callsign_column = 24; } if (no_date_flag == FALSE && no_time_flag == FALSE) { sprintf (opline,"% 9s % 4s % 8s% 4s %s\n", date, time, band, mode, call); callsign_column = 29; } } else { if (no_date_flag == TRUE && no_time_flag == TRUE) { sprintf (opline,"% 8s% 4s %s %s\n", band, mode, call, comment); callsign_column = 14; } if (no_date_flag == TRUE && no_time_flag == FALSE) { sprintf (opline,"% 4s % 8s% 4s %s %s\n", time, band, mode, call, comment); callsign_column = 19; } if (no_date_flag == FALSE && no_time_flag == TRUE) { sprintf (opline,"% 9s % 8s% 4s %s %s\n", date, band, mode, call, comment); callsign_column = 24; } if (no_date_flag == FALSE && no_time_flag == FALSE) { sprintf (opline,"% 9s % 4s % 8s% 4s %s %s\n", date, time, band, mode, call, comment); callsign_column = 29; } } /* Save QSO details to temp file */ write_temp_file (call[0], opline); } fprintf (stderr, "\n\n%d QSOs processed\n", qso_count); } void read_Cabrillo_line (char *line, int qso_count) { char call[80]; char date[80]; char time[80]; char band[80]; char mode[80]; char comment[80]; char opline [80]; char *p; int values; /* each Cabrillo row starts with QSO: */ p = strstr (line, "QSO:"); if (p == NULL) return; /* Clear out the previous values */ strcpy (date, " "); strcpy (time, " "); strcpy (band, " "); strcpy (mode, " "); strcpy (call, " "); strcpy (comment, " "); /* Cabrillo (anonyingly) has a number of different formats dependent on the */ /* contest. Thus need to check which format and how many columns in each of */ /* rows */ switch (cabrillo_contest) { case CBR_ARRLVHF: /* IOTA has 8 columns */ values = sscanf (p, "QSO: %s %s %s %s %s %s %s %s", &band, &mode, &date, &time, &comment, &comment, &call, &comment); break; case CBR_IARU: case CBR_APSPRINT: case CBR_ARRL10: case CBR_ARRL160: case CBR_ARRLDX: case CBR_CQWPX: /* IOTA has 10 columns */ values = sscanf (p, "QSO: %s %s %s %s %s %s %s %s %s %s", &band, &mode, &date, &time, &comment, &comment, &comment, &call, &comment, &comment); break; case CBR_IOTA: case CBR_CQWWRTTY: /* IOTA has 12 columns */ values = sscanf (p, "QSO: %s %s %s %s %s %s %s %s %s %s %s %s", &band, &mode, &date, &time, &comment, &comment, &comment, &comment, &call, &comment, &comment, &comment); break; case CBR_RSGB2128: /* RSGB 21/28 MHz has 12 columns */ values = sscanf (p, "QSO: %s %s %s %s %s %s %s %s %s %s %s %s %s", &band, &mode, &date, &time, &comment, &comment, &comment, &call, &comment, &comment, &comment, &comment); break; case CBR_KIDS: /* Kids Roundup has 12 columns */ values = sscanf (p, "QSO: %s %s %s %s %s %s %s %s %s %s %s %s", &band, &mode, &date, &time, &comment, &comment, &comment, &comment, &call, &comment, &comment, &comment); break; case CBR_SWEEPSTAKES: /* Sweepstakes has 14 columns */ values = sscanf (p, "QSO: %s %s %s %s %s %s %s %s %s %s %s %s %s %s", &band, &mode, &date, &time, &comment, &comment, &comment, &comment, &comment, &call, &comment, &comment, &comment, &comment); break; default: /* Default has 8 columns */ /* values = sscanf (p, "QSO: %s %s %s %s %s %s %s %s", &band, &mode, &date, &time, &comment, &call, &comment, &comment);*/ /* These contests have 10 columns */ values = sscanf (p, "QSO: %s %s %s %s %s %s %s %s %s %s", &band, &mode, &date, &time, &comment, &comment, &comment, &call, &comment, &comment); } if (values == EOF) { fprintf (stderr,"sortlog: Internal Error - please e-mail log to G4ZFE!!\n"); exit (1); } /* Convert the mode to one of the following as per LoTW - CW/PHO/IMG/DAT */ convert_mode (&mode, &mode); /* Display some progress! */ if (qso_count % 100 == 0) putchar ('.'); if (qso_count % 8000 == 0) putchar ('\n'); /* Write to temporary file. Don't forget to strip off date and time */ /* info if this has been requested */ /* format "band" as 8 characters as it is the frequency */ if (no_date_flag == TRUE && no_time_flag == TRUE) { sprintf (opline,"% 8s% 4s %s\n", band, mode, call); callsign_column = 14; } if (no_date_flag == TRUE && no_time_flag == FALSE) { sprintf (opline,"% 4s % 8s% 4s %s\n", time, band, mode, call); callsign_column = 19; } if (no_date_flag == FALSE && no_time_flag == TRUE) { sprintf (opline,"% 9s % 8s% 4s %s\n", date, band, mode, call); callsign_column = 24; } if (no_date_flag == FALSE && no_time_flag == FALSE) { sprintf (opline,"% 9s % 4s % 8s% 4s %s\n", date, time, band, mode, call); callsign_column = 29; } /* Save QSO details to temp file */ write_temp_file (call[0], opline); } void process_Cabrillo_format (FILE* in, char *iline) { char ipline [255]; char line[255]; int start = FALSE; char contest[80]; int i; int values; long qso_count = 0; if (strlen(iline) > 1) { line[strlen(iline)-1] = '\0'; } /* Convert to upper case */ for (i=0; i <= (int) strlen(iline); i++) ipline[i] = toupper (iline[i]); /* Check if Cabrillo format */ if (strstr (ipline,"SOAPBOX:") != NULL) { /* End of Header found - no need to skip */ } else { /* Skip Cabrillo header info */ while (start == FALSE) { if (fgets (ipline, sizeof(ipline), in) == NULL) return; /* Convert to upper case */ for (i=0; i <= (int) strlen(ipline); i++) line[i] = toupper (ipline[i]); if (strstr (line, "CONTEST:") != NULL) { /* Check for IOTA contest as this has additional columns */ values = sscanf (line, "CONTEST: %s ",&contest); if (strstr (contest, "RSGB-IOTA") != NULL) cabrillo_contest = CBR_IOTA; else if (strstr (contest, "ARRL-VHF-SEP") != NULL) cabrillo_contest = CBR_ARRLVHF; else if (strstr (contest, "CQ-WW-RTTY") != NULL) cabrillo_contest = CBR_CQWWRTTY; else if (strstr (contest, "ARRL-SS-CW") != NULL) cabrillo_contest = CBR_SWEEPSTAKES; else if (strstr (contest, "IARU") != NULL) cabrillo_contest = CBR_IARU; else if (strstr (contest, "AP-SPRINT") != NULL) cabrillo_contest = CBR_APSPRINT; else if (strstr (contest, "ARRL-10") != NULL) cabrillo_contest = CBR_ARRL10; else if (strstr (contest, "ARRL-160") != NULL) cabrillo_contest = CBR_ARRL160; else if (strstr (contest, "ARRL-DX") != NULL) cabrillo_contest = CBR_ARRLDX; else if (strstr (contest, "CQ-WPX") != NULL) cabrillo_contest = CBR_CQWPX; else if (strstr (contest, "RSGB 10") != NULL) cabrillo_contest = CBR_RSGB2128; else if (strstr (contest, "RSGB 21") != NULL) cabrillo_contest = CBR_RSGB2128; else if (strstr (contest, "KIDS") != NULL) cabrillo_contest = CBR_KIDS; else cabrillo_contest = CBR_DEFAULT; } /* Continue reading the header until we get to the QSO data */ if (strstr (line, "QSO:") != NULL) start = TRUE; } } fprintf (stderr,"\nCabrillo format detected\n"); fprintf (stderr, "\nEach dot is 100 QSOs. Writing logs\n"); qso_count++; read_Cabrillo_line (line, qso_count); ipline [0] = '\0'; /* Read in the rest of the input file */ while (fgets (ipline, sizeof(ipline), in)) { /* Skip blank lines */ while (strlen (ipline) <= 1) if (fgets (ipline, sizeof(ipline), in) == NULL) return; ipline [strlen(ipline)] = '\0'; /* Convert to upper case */ for (i=0; i <= (int) strlen(ipline); i++) line[i] = toupper (ipline[i]); /* Check if the end of the log has been reached */ if (strstr (line, "END-OF-LOG:") != NULL) { fprintf (stderr, "\n\n%d QSOs processed\n", qso_count); return; } qso_count++; read_Cabrillo_line (line, qso_count); } } void sort_logs () { char sort_cmd [80]; char ch; /* Sort the temporary files */ for (ch='0'; ch<='Z'; ch++) { if (!isalnum(ch)) continue; /* Display some progress */ if (lower_case_filenames_flag == TRUE) putchar (tolower(ch)); else putchar (ch); /* Determine which column the callsign */ /* is in so we can sort on the callsign */ if (lower_case_filenames_flag == TRUE) sprintf (sort_cmd," sort /+%d < %c.tmp > %c.txt",callsign_column, tolower(ch),tolower(ch)); else sprintf (sort_cmd," sort /+%d < %c.TMP > %c.TXT",callsign_column, ch,ch); /* Sort the file by callsign column */ system (sort_cmd); /* Delete the temporary file */ if (lower_case_filenames_flag == TRUE) sprintf (sort_cmd, "%c.tmp",tolower(ch)); else sprintf (sort_cmd, "%c.TMP",ch); remove (sort_cmd); } putchar ('\n'); } int main (int argc, char **argv) { FILE *in, *out; int i=0; int j=0; char ipline[512]; char line[512]; char opline [80]; char *p; char data[20][30]; char filename [80]; long qso_numb = 0; char txt_file [6]; char ch; /* Check that a filename argument has been passed */ if (argc < 2) { fprintf (stderr, "usage: sortlog [-nodate | -date | -notime | -time] [-comment -lower] filename\n\n"); fprintf (stderr, " -nodate: produce output files with no date information (default)\n"); fprintf (stderr, " -date: produce output files with date information\n"); fprintf (stderr, " -notime: produce output files with no time information (default)\n"); fprintf (stderr, " -time: produce output files with time information\n"); fprintf (stderr, " -comment: leave ADIF commemt lines in the output files\n"); fprintf (stderr, " -lower: create output files with lower case filenames\n"); fprintf (stderr, "For Internet logs it is recommended to use the default -nodate and -notime parameters\n"); exit (1); } if (argv [1][0] == '-' && (argv [1][1] == 'v' || argv [1][1] == 'V')) { fprintf (stderr,"G4ZFE sortlog: Version 1.27 - 05/MAR/2006\n"); exit (0); } /* Check for command line switches */ if (argc >= 2) { for (i=1; i < argc; i++) { if (argv[i][0] == '-') { for (j=1; j <= (int) strlen(argv[1]); j++) argv[i][j] = toupper (argv[i][j]); if (!strcmp (argv[i], "-DATE")) no_date_flag = FALSE; if (!strcmp (argv[i], "-TIME")) no_time_flag = FALSE; if (!strcmp (argv[i], "-COMMENT")) comment_flag = TRUE; if (!strcmp (argv[i], "-LOWER")) lower_case_filenames_flag = TRUE; if (!strcmp (argv[i], "-HELP") || !strcmpi (argv[i], "-h") || !strcmpi (argv[i], "-?")) { fprintf (stderr, "usage: sortlog [-nodate | -date | -notime | -time] [-comment -lower] filename\n\n"); fprintf (stderr, " -nodate: produce output files with no date information (default)\n"); fprintf (stderr, " -date: produce output files with date information\n"); fprintf (stderr, " -notime: produce output files with no time information (default)\n"); fprintf (stderr, " -time: produce output files with time information\n"); fprintf (stderr, " -comment: leave ADIF commemt lines in the output files\n"); fprintf (stderr, " -lower: create output files with lower case filenames\n"); fprintf (stderr, "For Internet logs it is recommended to use the default -nodate and -notime parameters\n"); exit (1); } } } } strcpy (filename, ""); for (i=1; i < argc; i++) { if (argv[i][0] != '-') strcpy (filename, argv[i]); } /* Open the log file */ if ( (in = fopen(filename, "r")) == NULL) { fprintf (stderr, "unable to open file %s\n", filename); exit (1); } i = 0; /* Open the temporary files */ for (ch='0'; ch<='Z'; ch++) { if (!isalnum(ch)) continue; if (lower_case_filenames_flag == TRUE) sprintf (txt_file,"%c.tmp",tolower(ch)); else sprintf (txt_file,"%c.TMP",ch); /* Create the temporary file */ if ( (out = fopen (txt_file, "a")) == NULL) { fprintf(stderr,"unable to create file %s\n", txt_file); exit (1); } /* If this has been assigned one of the "common" */ /* callsigns then remember the stream for later, */ /* otherwise close the stream. */ switch (ch) { case 'K': case 'k': temp_files[0] = out; break; case 'O': case 'o': temp_files[1] = out; break; case 'W': case 'w': temp_files[2] = out; break; case 'D': case 'd': temp_files[3] = out; break; case 'U': case 'u': temp_files[4] = out; break; case 'S': case 's': temp_files[5] = out; break; case 'N': case 'n': temp_files[6] = out; break; case 'R': case 'r': temp_files[7] = out; break; case 'H': case 'h': temp_files[8] = out; break; case 'Y': case 'y': temp_files[9] = out; break; case 'I': case 'i': temp_files[10] = out; break; case 'L': case 'l': temp_files[11] = out; break; case 'G': case 'g': temp_files[12] = out; break; default: fclose (out); } i++; } /* Read line 1 to guess the format */ fgets (ipline, sizeof(ipline), in); /* Skip invalid lines */ while (!check_valid_line (ipline)) if (fgets (ipline, sizeof(ipline), in) == NULL) exit (0); if (ADIF_format) { /* Read ADIF format file into temporary files */ process_ADIF_format (in, ipline); fclose (in); /* Close the temporary files */ close_temp_file (); fprintf (stderr, "\nSorting logs....\n"); sort_logs (); /* We're done */ exit (0); } if (Cabrillo_format) { /* Read Cabrillo format file into temporary files */ process_Cabrillo_format (in, ipline); fclose (in); /* Close the temporary files */ close_temp_file (); fprintf (stderr, "\nSorting logs....\n"); sort_logs (); /* We're done */ exit (0); } /* Convert to upper case */ for (i=0; i <= (int) strlen(ipline); i++) line[i] = toupper (ipline[i]); p = strtok (line, " "); strcpy (data[column], p); parse_line (p); while ( (p = strtok (NULL, " ")) != NULL) { column++; strcpy (data[column], p); parse_line (p); } /* Trim frequency, e.g 14.200 to 14. This saves */ /* space! */ if (strcmp(data[band_pos],"1.8") && strcmp(data[band_pos],"3.5") && strcmp(data[band_pos],"3.6") && strcmp(data[band_pos],"3.8") && strcmp(data[band_pos],"10.1") && strcmp(data[band_pos],"24.5")) { p = strchr (&data[band_pos][0], '.'); if (p) *p = '\0'; } /* If a column was not found then just display */ /* blank characters */ if (date_pos == -1) strcpy (data[date_pos],""); if (time_pos == -1) strcpy (data[time_pos],""); if (band_pos == -1) strcpy (data[band_pos],""); if (mode_pos == -1) strcpy (data[mode_pos],""); if (call_pos == -1) strcpy (data[call_pos],""); /* Convert the mode to one of the following as per LoTW - CW/PHO/IMG/DAT */ convert_mode (&data[mode_pos], &data[mode_pos]); /* Write first line to the temp file */ if (no_date_flag == TRUE && no_time_flag == TRUE) { sprintf (opline,"% 3s% 4s %s\n", data [band_pos], data [mode_pos], data [call_pos]); callsign_column = 9; } if (no_date_flag == TRUE && no_time_flag == FALSE) { sprintf (opline,"% 4s % 3s% 4s %s\n", data [time_pos], data [band_pos], data [mode_pos], data [call_pos]); callsign_column = 14; } if (no_date_flag == FALSE && no_time_flag == TRUE) { sprintf (opline,"% 9s % 3s% 4s %s\n", data [date_pos], data [band_pos], data [mode_pos], data [call_pos]); callsign_column = 19; } if (no_date_flag == FALSE && no_time_flag == FALSE) { sprintf (opline,"% 9s % 4s % 3s% 4s %s\n", data [date_pos], data [time_pos], data [band_pos], data [mode_pos], data [call_pos]); callsign_column = 24; } write_temp_file (data [call_pos][0], opline); qso_numb++; fprintf (stderr, "\nEach dot is 100 QSOs. Writing logs\n"); /* Read in the rest of the input file */ while (fgets (ipline, sizeof(ipline), in) != NULL) { if (!check_valid_line (ipline)) continue; qso_numb++; /* Display some progress! */ if (qso_numb % 100 == 0) putchar ('.'); if (qso_numb % 8000 == 0) putchar ('\n'); /* Convert to upper case */ for (i=0; i <= (int) strlen(ipline); i++) line[i] = toupper (ipline[i]); column = 0; p = strtok (line, " "); strcpy (data[column], p); while ( (p = strtok (NULL, " ")) != NULL) { column++; strcpy (data[column], p); } /* Trim frequency, e.g 14.200 to 14 This saves */ /* space! */ if (strcmp(data[band_pos],"1.8") && strcmp(data[band_pos],"3.5") && strcmp(data[band_pos],"3.6") && strcmp(data[band_pos],"3.8") && strcmp(data[band_pos],"10.1") && strcmp(data[band_pos],"24.5")) { p = strchr (&data [band_pos][0],'.'); if (p) *p = '\0'; } /* If a column was not found then just display */ /* blank characters */ if (date_pos == -1) strcpy (data[date_pos],""); if (time_pos == -1) strcpy (data[time_pos],""); if (band_pos == -1) strcpy (data[band_pos],""); if (mode_pos == -1) strcpy (data[mode_pos],""); if (call_pos == -1) strcpy (data[call_pos],""); /* Trim PSK31 to PSK to fit into 4 bytes and avoid */ /* code re-write. Sorry I am lazy! */ if (! strcmp (data[mode_pos],"PSK31") || ! strcmp (data[mode_pos],"PSK63") || !strcmp (data[mode_pos],"BPSK63") ) strcpy (data[mode_pos],"PSK"); /* ditto RTTY */ if (! strcmp (data[mode_pos],"RTTY")) strcpy (data[mode_pos],"RY"); /* ditto MFSK16 */ if (! strcmp (data[mode_pos],"MFSK16")) strcpy (data[mode_pos],"MFSK"); /* ditto all the WSJT modes */ if (! strcmp (data[mode_pos],"WSJT") || ! strcmp (data[mode_pos],"FSK441") || ! strcmp (data[mode_pos],"JT6M") || ! strcmp (data[mode_pos],"JT44")) strcpy (data[mode_pos], "JT"); /* Write each line to the temp file */ if (no_date_flag == TRUE && no_time_flag == TRUE) { sprintf (opline,"% 3s% 4s %s\n", data [band_pos], data [mode_pos], data [call_pos]); callsign_column = 9; } if (no_date_flag == TRUE && no_time_flag == FALSE) { sprintf (opline,"% 4s % 3s% 4s %s\n", data [time_pos], data [band_pos], data [mode_pos], data [call_pos]); callsign_column = 14; } if (no_date_flag == FALSE && no_time_flag == TRUE) { sprintf (opline,"% 9s % 3s% 4s %s\n", data [date_pos], data [band_pos], data [mode_pos], data [call_pos]); callsign_column = 19; } if (no_date_flag == FALSE && no_time_flag == FALSE) { sprintf (opline,"% 9s % 4s % 3s% 4s %s\n", data [date_pos], data [time_pos], data [band_pos], data [mode_pos], data [call_pos]); callsign_column = 24; } write_temp_file (data [call_pos][0], opline); } fprintf (stderr,"\n\n%ld QSOs read\n", qso_numb); fclose (in); /* Close the temporary files */ close_temp_file (); fprintf (stderr, "\nSorting logs....\n"); sort_logs (); return (0); }