/*
Weather Protocol plugin for Miranda IM
Copyright (c) 2012 Miranda NG team
Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
Copyright (c) 2002-2005 Calvin Che
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; version 2
of the License.
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 .
*/
/*
This file contain the source related unit conversion, icon assignment,
string conversions, display text parsing, etc
*/
#include "stdafx.h"
/////////////////////////////////////////////////////////////////////////////////////////
// see if a string is a number
// s = the string to be determined
// return value = true if the string is a number, false if it isn't
BOOL is_number(const wchar_t *s)
{
BOOL tag = FALSE;
// looking character by character
// for a number: numerous spaces in front, then optional +/-, then the number
// don't care anything that comes after it
while (*s != '\0') {
if (*s >= '0' && *s <= '9') return TRUE;
else if (*s == ' ');
else if (*s != '+' && *s != '-') return FALSE;
else if ((*s == '+' || *s == '-') && !tag) tag = TRUE;
else return FALSE;
s++;
}
return FALSE;
}
void CWeatherProto::numToStr(double num, wchar_t *str, size_t strSize)
{
int i = (int)(num * (opt.NoFrac ? 10 : 100));
int u = abs(i);
int r = u % 10;
int w = u / 10 + (r >= 5);
if (opt.NoFrac)
r = 0;
else {
r = w % 10;
w /= 10;
}
if (i < 0 && (w || r)) w = -w;
if (r)
mir_snwprintf(str, strSize, L"%i.%i", w, r);
else
mir_snwprintf(str, strSize, L"%i", w);
}
/////////////////////////////////////////////////////////////////////////////////////////
// temperature conversion
// tempchar = the string containing the temperature value
// unit = the unit for temperature
// return value = the converted temperature with degree sign and unit; if fails, return N/A
void CWeatherProto::GetTemp(const wchar_t *tempchar, const wchar_t *unit, wchar_t *str)
{
// unit can be C, F
double temp;
wchar_t tstr[20];
// quit if the value obtained is N/A or not a number
if (!mir_wstrcmp(tempchar, NODATA) || !mir_wstrcmp(tempchar, L"N/A")) {
mir_wstrcpy(str, tempchar);
return;
}
if (!is_number(tempchar)) {
mir_wstrcpy(str, NODATA);
return;
}
// convert the string to an integer
temp = _wtof(tempchar);
// convert all to F first
if (!mir_wstrcmpi(unit, L"C")) temp = (temp * 9 / 5) + 32;
else if (!mir_wstrcmpi(unit, L"K")) temp = ((temp - 273.15) * 9 / 5) + 32;
// convert to apporiate unit
switch (opt.tUnit) {
case 1:
// rounding
numToStr((temp - 32) / 9 * 5, tstr, _countof(tstr));
if (opt.DoNotAppendUnit)
wcsncpy_s(str, MAX_DATA_LEN, tstr, _TRUNCATE);
else
mir_snwprintf(str, MAX_DATA_LEN, L"%s%sC", tstr, opt.DegreeSign);
break;
case 2:
numToStr(temp, tstr, _countof(tstr));
if (opt.DoNotAppendUnit)
wcsncpy_s(str, MAX_DATA_LEN, tstr, _TRUNCATE);
else
mir_snwprintf(str, MAX_DATA_LEN, L"%s%sF", tstr, opt.DegreeSign);
break;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
// temperature conversion
// tempchar = the string containing the pressure value
// unit = the unit for pressure
// return value = the converted pressure with unit; if fail, return the original string
void CWeatherProto::GetPressure(const wchar_t *tempchar, const wchar_t *unit, wchar_t *str)
{
// unit can be kPa, hPa, mb, in, mm, torr
double tempunit = 0, output;
int intunit;
// convert the string to a floating point number (always positive)
// if it end up with 0, then it's not a number, return the original string and quit
output = _wtof(tempchar);
if (output == 0) {
mir_wstrcpy(str, tempchar);
return;
}
// convert all to mb first
if (!mir_wstrcmpi(unit, L"KPA"))
tempunit = (double)output * 10;
else if (!mir_wstrcmpi(unit, L"HPA"))
tempunit = (double)output;
else if (!mir_wstrcmpi(unit, L"MB"))
tempunit = (double)output;
else if (!mir_wstrcmpi(unit, L"IN"))
tempunit = (double)output * 33.86388;
else if (!mir_wstrcmpi(unit, L"MM"))
tempunit = (double)output * 1.33322;
else if (!mir_wstrcmpi(unit, L"TORR"))
tempunit = (double)output * 1.33322;
// convert to apporiate unit
switch (opt.pUnit) {
case 1:
intunit = (int)(tempunit + 0.5);
mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("kPa"));
break;
case 2:
intunit = (int)(tempunit + 0.5);
mir_snwprintf(str, MAX_DATA_LEN, L"%i %s", intunit, opt.DoNotAppendUnit ? L"" : TranslateT("mb"));
break;
case 3:
intunit = (int)((tempunit * 10 / 33.86388) + 0.5);
mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("in"));
break;
case 4:
intunit = (int)((tempunit * 10 / 1.33322) + 0.5);
mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("mm"));
break;
default:
mir_wstrcpy(str, tempchar);
break;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
// speed conversion
// tempchar = the string containing the speed value
// unit = the unit for speed
// return value = the converted speed with unit; if fail, return _T(""
void CWeatherProto::GetSpeed(const wchar_t *tempchar, const wchar_t *unit, wchar_t *str)
{
// unit can be km/h, mph, m/s, knots
double tempunit;
wchar_t tstr[20];
str[0] = 0;
// convert the string into an integer (always positive)
// if the result is 0, then the string is not a number, return _T(""
tempunit = _wtof(tempchar);
if (tempunit == 0 && tempchar[0] != '0')
return;
// convert all to m/s first
if (!mir_wstrcmpi(unit, L"KM/H"))
tempunit /= 3.6;
// else if ( !mir_wstrcmpi(unit, L"M/S")
// tempunit = tempunit;
else if (!mir_wstrcmpi(unit, L"MPH"))
tempunit *= 0.44704;
else if (!mir_wstrcmpi(unit, L"KNOTS"))
tempunit *= 0.514444;
// convert to apporiate unit
switch (opt.wUnit) {
case 1:
numToStr(tempunit * 3.6, tstr, _countof(tstr));
mir_snwprintf(str, MAX_DATA_LEN, L"%s %s", tstr, opt.DoNotAppendUnit ? L"" : TranslateT("km/h"));
break;
case 2:
numToStr(tempunit, tstr, _countof(tstr));
mir_snwprintf(str, MAX_DATA_LEN, L"%s %s", tstr, opt.DoNotAppendUnit ? L"" : TranslateT("m/s"));
break;
case 3:
numToStr(tempunit / 0.44704, tstr, _countof(tstr));
mir_snwprintf(str, MAX_DATA_LEN, L"%s %s", tstr, opt.DoNotAppendUnit ? L"" : TranslateT("mph"));
break;
case 4:
numToStr(tempunit / 0.514444, tstr, _countof(tstr));
mir_snwprintf(str, MAX_DATA_LEN, L"%s %s", tstr, opt.DoNotAppendUnit ? L"" : TranslateT("knots"));
break;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
// distance conversion
// tempchar = the string containing the distance value
// unit = the unit for distance
// return value = the converted distance with unit; if fail, return original string
void CWeatherProto::GetDist(const wchar_t *tempchar, const wchar_t *unit, wchar_t *str)
{
// unit can be km, miles
double tempunit = 0, output;
int intunit;
// convert the string to a floating point number (always positive)
// if it end up with 0, then it's not a number, return the original string and quit
output = _wtof(tempchar);
if (output == 0) {
mir_wstrcpy(str, tempchar);
return;
}
// convert all to km first
if (!mir_wstrcmpi(unit, L"KM"))
tempunit = (double)output;
else if (!mir_wstrcmpi(unit, L"MILES"))
tempunit = (double)output * 1.609;
// convert to apporiate unit
switch (opt.vUnit) {
case 1:
intunit = (int)((tempunit * 10) + 0.5);
mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("km"));
break;
case 2:
intunit = (int)((tempunit * 10 / 1.609) + 0.5);
mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("miles"));
break;
default:
mir_wstrcpy(str, tempchar);
break;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
// elevation conversion
// tempchar = the string containing the elevation value
// unit = the unit for elevation
// return value = the converted elevation with unit; if fail, return original string
void CWeatherProto::GetElev(const wchar_t *tempchar, const wchar_t *unit, wchar_t *str)
{
// unit can be ft, m
double tempunit = 0, output;
int intunit;
// convert the string to a floating point number (always positive)
// if it end up with 0, then it's not a number, return the original string and quit
output = _wtof(tempchar);
if (output == 0) {
mir_wstrcpy(str, tempchar);
return;
}
// convert all to m first
if (!mir_wstrcmpi(unit, L"M"))
tempunit = (double)output;
else if (!mir_wstrcmpi(unit, L"FT"))
tempunit = (double)output / 3.28;
// convert to apporiate unit
switch (opt.eUnit) {
case 1:
intunit = (int)((tempunit * 10 * 3.28) + 0.5);
mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("ft"));
break;
case 2:
intunit = (int)((tempunit * 10) + 0.5);
mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("m"));
break;
default:
mir_wstrcpy(str, tempchar);
break;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
// this function convert the string to the format with 1 upper case followed by lower case char
void CaseConv(wchar_t *str)
{
bool nextUp = true;
CharLowerBuffW(str, (uint32_t)mir_wstrlen(str));
for (wchar_t *pstr = str; *pstr; pstr++) {
if (*pstr == ' ' || *pstr == '-')
nextUp = true;
else if (nextUp) {
CharUpperBuffW(pstr, 1);
nextUp = false;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
// the next 2 functions are copied from miranda source
// str = the string to modify
void TrimString(char *str)
{
size_t len, start;
len = mir_strlen(str);
while (len && (unsigned char)str[len - 1] <= ' ') str[--len] = 0;
for (start = 0; (unsigned char)str[start] <= ' ' && str[start]; start++);
memmove(str, str + start, len - start + 1);
}
void TrimString(wchar_t *str)
{
size_t len, start;
len = mir_wstrlen(str);
while (len && (unsigned char)str[len - 1] <= ' ') str[--len] = 0;
for (start = 0; (unsigned char)str[start] <= ' ' && str[start]; start++);
memmove(str, str + start, (len - start + 1) * sizeof(wchar_t));
}
/////////////////////////////////////////////////////////////////////////////////////////
// convert \t to tab and \n to linefeed
void ConvertBackslashes(char *str)
{
for (char *pstr = str; *pstr; pstr = CharNextA(pstr)) {
if (*pstr == '\\') {
switch (pstr[1]) {
case 'n': *pstr = '\n'; break;
case 't': *pstr = '\t'; break;
default: *pstr = pstr[1]; break;
}
memmove(pstr + 1, pstr + 2, mir_strlen(pstr + 2) + 1);
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
// replace spaces with _T("%20"
// dis = original string
// return value = the modified string with space -> _T("%20"
char* GetSearchStr(char *dis)
{
char *pstr = dis;
size_t len = mir_strlen(dis);
while (*pstr != 0) {
if (*pstr == ' ') {
memmove(pstr + 3, pstr + 1, len);
memcpy(pstr, L"%20", 3);
pstr += 2;
}
pstr++;
len--;
}
return dis;
}
/////////////////////////////////////////////////////////////////////////////////////////
// make display and history strings
// w = WEATHERINFO data to be parsed
// dis = the string to parse
// return value = the parsed string
CMStringW GetDisplay(WEATHERINFO *w, const wchar_t *dis)
{
wchar_t lpzDate[32], chr;
char name[256], temp[2];
DBVARIANT dbv;
size_t i;
// Clear the string
CMStringW str;
// looking character by character
for (i = 0; i < mir_wstrlen(dis); i++) {
// for the escape characters
if (dis[i] == '\\') {
i++;
chr = dis[i];
switch (chr) {
case '%': str.Append(L"%"); break;
case 't': str.Append(L"\t"); break;
case 'n': str.Append(L"\r\n"); break;
case '\\': str.Append(L"\\"); break;
}
}
// for the % varaibles
else if (dis[i] == '%') {
i++;
chr = dis[i];
// turn capitalized characters to small case
if (chr < 'a' && chr != '[' && chr != '%') chr = (char)((int)chr + 32);
switch (chr) {
case 'c': str.Append(w->cond); break;
case 'd': // get the current date
GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, nullptr, nullptr, lpzDate, _countof(lpzDate));
str.Append(lpzDate); break;
case 'e': str.Append(w->dewpoint); break;
case 'f': str.Append(w->feel); break;
case 'h': str.Append(w->high); break;
case 'i': str.Append(w->winddir); break;
case 'l': str.Append(w->low); break;
case 'm': str.Append(w->humid); break;
case 'n': str.Append(w->city); break;
case 'p': str.Append(w->pressure); break;
case 'r': str.Append(w->sunrise); break;
case 's': str.Append(w->id); break;
case 't': str.Append(w->temp); break;
case 'u':
if (mir_wstrcmp(w->update, NODATA))
str.Append(w->update);
else
str.Append(TranslateT(""));
break;
case 'v': str.Append(w->vis); break;
case 'w': str.Append(w->wind); break;
case 'y': str.Append(w->sunset); break;
case '%': str.Append(L"%"); break;
case '[': // custom variables
i++;
name[0] = 0;
// read the entire variable name
while (dis[i] != ']' && i < mir_wstrlen(dis)) {
mir_snprintf(temp, "%c", dis[i++]);
mir_strcat(name, temp);
}
// access the database to get its value
if (!db_get_ws(w->hContact, WEATHERCONDITION, name, &dbv)) {
if (dbv.pwszVal != TranslateW(NODATA) && dbv.pwszVal != TranslateT(""))
str.Append(dbv.pwszVal);
db_free(&dbv);
}
break;
}
}
// if the character is not a variable, write the original character to the new string
else str.AppendChar(dis[i]);
}
return str;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Get the text when an error code is specified
// code = the error code obtained when updating weather
// str = the string for the error
wchar_t *GetError(int code)
{
wchar_t *str, str2[100];
switch (code) {
case 10: str = TranslateT("Invalid ID format, missing \"/\" (10)"); break;
case 11: str = TranslateT("Invalid service (11)"); break;
case 12: str = TranslateT("Invalid station (12)"); break;
case 20: str = TranslateT("Weather service ini for this station is not found (20)"); break;
case 30: str = TranslateT("Netlib error - check your internet connection (30)"); break;
case 40: str = TranslateT("Empty data is retrieved (40)"); break;
case 42: str = TranslateT("Document not found (42)"); break;
case 43: str = TranslateT("Document too short to contain any weather data (43)"); break;
case 99: str = TranslateT("Unknown error (99)"); break;
// 100 Continue
// 101 Switching Protocols
// 200 OK
// 201 Created
// 202 Accepted
// 203 Non-Authoritative Information
case 204: str = TranslateT("HTTP Error: No content (204)"); break;
// 205 Reset Content
// 206 Partial Content
// 300 Multiple Choices
case 301: str = TranslateT("HTTP Error: Data moved (301)"); break;
// 302 Found
// 303 See Other
// 304 Not Modified
case 305: str = TranslateT("HTTP Error: Use proxy (305)"); break;
case 307: str = TranslateT("HTTP Error: Temporary redirect (307)"); break;
case 400: str = TranslateT("HTTP Error: Bad request (400)"); break;
case 401: str = TranslateT("HTTP Error: Unauthorized (401)"); break;
case 402: str = TranslateT("HTTP Error: Payment required (402)"); break;
case 403: str = TranslateT("HTTP Error: Forbidden (403)"); break;
case 404: str = TranslateT("HTTP Error: Not found (404)"); break;
case 405: str = TranslateT("HTTP Error: Method not allowed (405)"); break;
// 406 Not Acceptable
case 407: str = TranslateT("HTTP Error: Proxy authentication required (407)"); break;
// 408 Request Timeout
// 409 Conflict
case 410: str = TranslateT("HTTP Error: Gone (410)"); break;
// 411 Length Required
// 412 Precondition Failed
// 413 Request Entity Too Large
// 414 Request-URI Too Long
// 415 Unsupported Media Type
// 416 Requested Range Not Satisfiable
// 417 Expectation Failed
case 500: str = TranslateT("HTTP Error: Internal server error (500)"); break;
// 501 Not Implemented
case 502: str = TranslateT("HTTP Error: Bad gateway (502)"); break;
case 503: str = TranslateT("HTTP Error: Service unavailable (503)"); break;
case 504: str = TranslateT("HTTP Error: Gateway timeout (504)"); break;
// 505 HTTP Version Not Supported
default:
mir_snwprintf(str2, TranslateT("HTTP Error %i"), code);
str = str2;
break;
}
return mir_wstrdup(str);
}