Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2136 - 1
/*****************************************************************************
2
 *   Copyright (C) 2013 Oliver Gemesi                                        *
3
 *                                                                           *
4
 *   This program is free software; you can redistribute it and/or modify    *
5
 *   it under the terms of the GNU General Public License as published by    *
6
 *   the Free Software Foundation; either version 2 of the License.          *
7
 *                                                                           *
8
 *   This program is distributed in the hope that it will be useful,         *
9
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
10
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
11
 *   GNU General Public License for more details.                            *
12
 *                                                                           *
13
 *   You should have received a copy of the GNU General Public License       *
14
 *   along with this program; if not, write to the                           *
15
 *   Free Software Foundation, Inc.,                                         *
16
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.               *
17
 *****************************************************************************/
18
 
19
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
20
//+ xutils.c - erweiterte String-Funktionen, xprintf (Info nach History)
21
//+            und weitere Hilfsfunktionen wie z.B. UTCdatetime2local()
22
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
23
 
24
//############################################################################
25
//# HISTORY  xutils.c
26
//#
27
//# 12.04.2014 OG
28
//# - chg: strncpyat(), strncpyat_P(), _strncpyat() erweitert um Parameter 'sepcharcount'
29
//#
30
//# 08.04.2014 OG
31
//# - add: strncpyat(), strncpyat_P(), _strncpyat()
32
//#
33
//# 28.02.2014 OG
34
//# - add: buffered_sprintf(), buffered_sprintf_P()
35
//# - add: buffer sprintf_buffer[]
36
//# - chg: PGMBUFF_SIZE und ARGBUFF_SIZE von 80 auf 40 geaendert
37
//#
38
//# 24.06.2013 OG
39
//# - add: strrtrim() entfernt Leerzeichen auf der rechten Seite
40
//#
41
//# 14.05.2013 OG
42
//# - chg: Kommentarkopf von UTCdatetime2local() aktualisiert
43
//#
44
//# 05.05.2013 OG
45
//# - chg: UTCdatetime2local() auf Config.timezone/summertime umgestellt
46
//# - add: include eeprom.h
47
//#
48
//# 04.05.2013 OG
49
//# - chg: umbenannt zu xutils.c
50
//#
51
//# 03.05.2013 OG
52
//# - add: UTCdatetime2local()
53
//# - fix: _xvsnprintf() Darstellung kleiner negativer Nachkommazahlen (-1<z<0)
54
//#
55
//# 29.04.2013 OG
56
//# - chg: Doku zu xprintf Ergaenzt bei 'bekannte Einschraenkungen'
57
//# - chg: includes reduziert auf das Notwendige
58
//#
59
//# 28.04.2013 OG - NEU
60
//############################################################################
61
 
62
 
63
//############################################################################
64
//# xprintf
65
//#
66
//# Diese Variante von printf ist angepasst auf das PKT:
67
//#
68
//#  - Unterstuetzung von Festkomma-Integer Anzeige
69
//#  - Overflow-Anzeige durch '*' wenn eine Zahl die Format-Maske sprengt
70
//#  - progmen wird optional unterstuetz fuer den format-String als auch fuer
71
//#     String Argumente
72
//#  - strikte Einhaltung von Laengen einer Format-Maske
73
//#    (ggf. wird die Ausgabe gekuerzt)
74
//#
75
//# In diesem Source sind nur die Basis-xprintf zum Erzeugen von formatierten
76
//# Strings. Die direkten Ausgabefunktionen auf den Screen sind in lcd.c
77
//# als lcdx_printf_at() und lcdx_printf_at_P().
78
//#
79
//# FORMAT ANGABEN:
80
//#
81
//#   %d: dezimal int signed (Rechtsbuendige Ausgabe)   (Wandlung via itoa)
82
//#       "%d"    arg: 1234 -> "1234"
83
//#       "%5d"   arg: 1234 -> " 1234"
84
//#       "%5.2d" arg: 1234 -> "   12.34"
85
//#       "%05d"  arg: 123  -> "00123"
86
//#       "%3.2d" arg: -13  -> " -0.13"
87
//#
88
//#   %u: dezimal int unsigned (Rechtsbuendige Ausgabe) (Wandlung via utoa)
89
//#       wie %d jedoch mittels utoa
90
//#
91
//#   %h: hex int unsigned    -> Hex-Zahl     Rechtsbuendig, Laenge wird unterstuetzt z.B. "%4h"
92
//#   %o: octal int unsigned  -> Octal-Zahl   Rechtsbuendig, Laenge wird unterstuetzt z.B. "%2o"
93
//#   %b: binary int unsigned -> Binaer-Zahl  Rechtsbuendig, Laenge wird unterstuetzt z.B. "%8b"
94
//#
95
//#   %ld, %lu, %lh, %lo, %lb:
96
//#       wie die obigen jedoch fuer long-zahlen (via ltoa, ultoa)
97
//#       nur wenn define USE_XPRINTF_LONG gesetzt ist!
98
//#       Bespiele: "%ld", "%5.6ld" usw.
99
//#
100
//#   %s: String aus RAM      -> Linksbuendig, Laenge wird unterstuetzt (z.B. "%8s")
101
//#   %S: String aus progmem  -> Linksbuendig, Laenge wird unterstuetzt (z.B. "%8s")
102
//#
103
//#   %c: einzelnes char      -> Linksbuendig, Laenge wird unterstuetzt (z.B. "%8s")
104
//#   %%: Ausgabe von "%"
105
//#
106
//#
107
//# BEISPIELE:
108
//#
109
//#  vorhanden in: osd/osdata.c, lcd/lcd.c
110
//#
111
//#
112
//# BEKANNTE EINSCHRAENKUNGEN:
113
//#
114
//#  1. padding '0' mit negativen Zahlen wenn padding aktiv ist ergibt z.B. "00-1.5"
115
//#  2. der Entwickler muss auf eine korrekte Format-Maske achten. Andernfalls
116
//#     kommt es zu nicht vorhersehbaren Ausgaben.
117
//#  3. nicht in ISR's verwenden - dazu muss ggf. eine Anpassung durchgefuert werden
118
//#
119
//# KOMPILIER OPTIONEN
120
//#
121
//#  define: USE_XPRINTF_LONG
122
//#    Unterstuetzung von long int / unsigned long int ('l' Modifier in Maske)
123
//     Wird in der main.h gesetzt
124
//############################################################################
125
 
126
#define USE_XPRINTF_LONG  // Unterstuetzung von long integer - Achtung! Muss fuer PKT gesetzt sein
127
                          // da in Sourcen verwendet!
128
 
129
 
130
#include <avr/pgmspace.h>
131
#include <stdio.h>
132
#include <stdlib.h>
133
#include <string.h>
134
#include <stdarg.h>
135
#include <stdbool.h>
136
 
137
#include "../main.h"
138
#include "../timer/timer.h"
139
#include "../eeprom/eeprom.h"
140
 
141
 
142
#define PGMBUFF_SIZE 40     // max. 40 chars bei format-String aus progmem
143
#define ARGBUFF_SIZE 40     // buffer fuer xprintf Parameter (strings, itoa, utoa) (fuer ltoa ggf. anpassen)
144
 
145
 
146
#define SPRINTF_BUFFER_SIZE  40                  // max. 40 Chars fuer den Buffer fuer xsnprintf(), xsnprintf_P()
147
char sprintf_buffer[SPRINTF_BUFFER_SIZE];
148
 
149
 
150
//----------------------
151
// xprintf parser data
152
//----------------------
153
typedef struct
154
{
155
    uint8_t  parse;     // true / false
156
    char cmd;           // char: d u h o b s S c %
157
    uint8_t  prelen;    // Vorkomma
158
    uint8_t  declen;    // decimal (Nachkomma)
159
    uint8_t  point;     // true / false
160
    uint8_t  uselong;   // true / false
161
    uint8_t  mask;      // true / false
162
    char pad;           // ' ' oder '0'
163
} xprintf_t;
164
 
165
 
166
//----------------------
167
// Buffers
168
//----------------------
169
char cmddigit[7];
170
char argbuff[ARGBUFF_SIZE];
171
char pgmbuff[PGMBUFF_SIZE];
172
 
173
 
174
//####################################################################################
175
 
176
 
177
//---------------------------------------------------------------------
178
// Basisfunktion von xprintf
179
// Doku: siehe oben
180
//---------------------------------------------------------------------
181
void _xvsnprintf( uint8_t useprogmem, char *buffer, uint8_t n, const char *format, va_list ap )
182
{
183
    const char  *p_fmt;                         // pointer auf den format-String
184
    char        *p_dst;                         // pointer auf den destination buffer
185
    const char  *p_str;                         // pointer auf einen arg-String wenn ein String ausgegeben werden soll
186
    char        *p_cmddigit = 0;                // pointer auf den Digit-Buffer fuer eine Maske (Laenge/Nachkommastellen)
187
    const char  *p_fmtbuff;                     // pointer auf den format-Buffer (fuer progmem)
188
    const char  *p_argbuff;                     // pointer auf den argbuffer
189
    uint8_t     fmtlen, arglen, overflow;
190
    uint8_t     i,j,dec;
191
    uint8_t     dstcnt;
192
    uint8_t     minus;
193
    xprintf_t   cmd;                            // parser Daten
194
 
195
    p_fmtbuff = format;
196
 
197
    if( useprogmem )                            // format von progmem in's RAM kopieren
198
    {
199
        strncpy_P( pgmbuff, format, PGMBUFF_SIZE);
200
        pgmbuff[PGMBUFF_SIZE-1] = 0;
201
        p_fmtbuff = pgmbuff;
202
    }
203
 
204
    cmd.parse = false;
205
    p_fmt     = p_fmtbuff-1;                    // -1 -> wird am Anfang der Schleife korrigiert
206
    p_dst     = buffer;
207
    dstcnt    = 0;
208
 
209
    do
210
    {
211
        if( dstcnt >= n )                       // max. Anzahl von Zeichen fuer Ziel 'buffer' erreicht?
212
            break;
213
 
214
        p_fmt++;                                // naechstes Zeichen von format
215
 
216
        //########################
217
        //# 1. PARSE
218
        //########################
219
 
220
        //------------------------
221
        // START: parse cmd
222
        //------------------------
223
        if( cmd.parse == false && *p_fmt == '%' )
224
        {
225
            memset( &cmd, 0, sizeof(xprintf_t) );       // init
226
            cmd.parse  = true;
227
            cmd.pad    = ' ';
228
            p_cmddigit = cmddigit;
229
            continue;
230
        }
231
 
232
        //------------------------
233
        // NO parse: copy char
234
        //------------------------
235
        if( cmd.parse == false )
236
        {
237
            *p_dst = *p_fmt;
238
            p_dst++;
239
            dstcnt++;
240
            continue;
241
        }
242
 
243
        //------------------------
244
        // set: pad (eine '0' ganz am Anfang der Formatmaske)
245
        //------------------------
246
        if( cmd.parse == true && *p_fmt == '0' && p_cmddigit == cmddigit )
247
        {
248
            cmd.pad = '0';
249
            continue;
250
        }
251
 
252
        //------------------------
253
        // set: vor/nach-kommastellen
254
        //------------------------
255
        if( cmd.parse == true && *p_fmt >= '0' && *p_fmt <= '9' )
256
        {
257
            *p_cmddigit = *p_fmt;
258
            p_cmddigit++;
259
            continue;
260
        }
261
 
262
        //------------------------
263
        // set: point
264
        //------------------------
265
        if( cmd.parse == true && *p_fmt == '.' && cmd.point == false )
266
        {
267
            cmd.point   = true;
268
            *p_cmddigit = 0;
269
            cmd.prelen  = atoi( cmddigit );
270
            p_cmddigit  = cmddigit;
271
            continue;
272
        }
273
 
274
        //------------------------
275
        // set: uselong
276
        //------------------------
277
        #ifdef  USE_XPRINTF_LONG
278
        if( cmd.parse == true && *p_fmt == 'l' )
279
        {
280
            cmd.uselong  = true;
281
            continue;
282
        }
283
        #endif
284
 
285
        //------------------------
286
        // END: parse cmd
287
        //------------------------
288
        if( cmd.parse == true && (*p_fmt == 'd' || *p_fmt == 'u' || *p_fmt == 'x' || *p_fmt == 'b' || *p_fmt == 'o' ||
289
                                  *p_fmt == 's' || *p_fmt == 'S' || *p_fmt == 'c' || *p_fmt == '%') )
290
        {
291
            cmd.cmd     = *p_fmt;
292
            cmd.parse   = false;
293
 
294
            *p_cmddigit = 0;
295
            if( cmd.point == false )    cmd.prelen = atoi(cmddigit);
296
            else                        cmd.declen = atoi(cmddigit);
297
 
298
            if( cmd.point || cmd.prelen>0 )
299
                cmd.mask  = true;
300
        }
301
 
302
 
303
        //########################
304
        //# 2. EXECUTE
305
        //########################
306
 
307
        //------------------------
308
        // exec cmd: "d,u,x,b,o"
309
        //------------------------
310
        if( cmd.cmd == 'd' || cmd.cmd == 'u' || cmd.cmd == 'x' || cmd.cmd == 'b' || cmd.cmd == 'o' )
311
        {
312
            if( cmd.uselong )
313
            {
314
#ifdef  USE_XPRINTF_LONG
315
                switch(cmd.cmd)
316
                {
317
                    case 'd': ltoa ( va_arg(ap, long)         , argbuff, 10); break;    // LONG dezimal int signed
318
                    case 'u': ultoa( va_arg(ap, unsigned long), argbuff, 10); break;    // LONG dezimal int unsigned
319
                    case 'x': ultoa( va_arg(ap, unsigned long), argbuff, 16); break;    // LONG hex int unsigned
320
                    case 'b': ultoa( va_arg(ap, unsigned long), argbuff,  2); break;    // LONG binary int unsigned
321
                    case 'o': ultoa( va_arg(ap, unsigned long), argbuff,  8); break;    // LONG octal int unsigned
322
                }
323
#endif
324
            }
325
            else
326
            {
327
                switch(cmd.cmd)
328
                {
329
                    case 'd': itoa( va_arg(ap, int)         , argbuff, 10); break;      // dezimal int signed
330
                    case 'u': utoa( va_arg(ap, unsigned int), argbuff, 10); break;      // dezimal int unsigned
331
                    case 'x': utoa( va_arg(ap, unsigned int), argbuff, 16); break;      // hex int unsigned
332
                    case 'b': utoa( va_arg(ap, unsigned int), argbuff,  2); break;      // binary int unsigned
333
                    case 'o': utoa( va_arg(ap, unsigned int), argbuff,  8); break;      // octal int unsigned
334
                }
335
            }
336
 
337
            minus    = (argbuff[0] == '-');
338
            arglen   = strlen(argbuff);
339
 
340
            fmtlen   = cmd.prelen + cmd.declen + (cmd.point ? 1 : 0);
341
            arglen   = strlen(argbuff);
342
 
343
            overflow = cmd.mask &&                                                      // Zahl zu gross -> "*" anzeigen statt der Zahl
344
                         (arglen > cmd.prelen + cmd.declen || cmd.prelen < 1+minus);
345
 
346
            if( overflow )                                                              // overflow: Zahl passt nicht in Maske
347
            {                                                                           //   -> zeige '*.*'
348
                for( i=0; (i < fmtlen) && (dstcnt < n); i++)
349
                {
350
                    if( cmd.point && i==cmd.prelen )    *p_dst = '.';
351
                    else                                *p_dst = '*';
352
                    p_dst++;
353
                    dstcnt++;
354
                }
355
            }
356
            else                                                                        // else: if( overflow )
357
            {
358
                if( !cmd.mask )                                                         // keine Maske: alles von der Zahl ausgeben
359
                    fmtlen = arglen;
360
 
361
                p_argbuff = argbuff;
362
                if( minus )                                                             // wenn Zahl negativ: merken und auf dem argbuff nehmen
363
                {
364
                    p_argbuff++;
365
                    arglen--;
366
                }
367
 
368
                //-----------------
369
                // die Zahl wird 'Rueckwaerts' uebertragen
370
                //-----------------
371
                dec = -1;                                                               // wird am Anfang der Schleife auf 0 gesetzt
372
                j   = 1;                                                                // zaehler fuer argbuff
373
                for( i=1; i<=fmtlen; i++ )
374
                {
375
                    dec++;                                                              // Zaehler Dizmalstellen
376
 
377
                    if( dstcnt+fmtlen-i <= n )                                          // wenn Zielbuffer nicht ueberschritten
378
                    {
379
                        if( cmd.point && (dec == cmd.declen) )                          // Dezimalpunkt setzen
380
                        {
381
                            p_dst[fmtlen-i] = '.';
382
                            continue;
383
                        }
384
 
385
                        if( j <= arglen )                                               // Ziffer uebertragen aus argbuff
386
                        {
387
                            p_dst[fmtlen-i] = p_argbuff[arglen-j];
388
                            j++;
389
                            continue;
390
                        }
391
 
392
                        if( cmd.declen > 0 &&                                           // Nachkomma und 1. Vorkommastelle ggf. auf '0'
393
                              (dec < cmd.declen || dec == cmd.declen+1) )               //   setzen wenn die Zahl zu klein ist
394
                        {
395
                            p_dst[fmtlen-i] = '0';
396
                            continue;
397
                        }
398
 
399
                        if( minus && ( (cmd.pad == ' ') ||                              // ggf. Minuszeichen setzen
400
                                       (cmd.pad != ' ' && i == fmtlen) ) )              //  Minuszeichen bei '0'-padding an erster Stelle setzen
401
                        {
402
                            minus = false;
403
                            p_dst[fmtlen-i] = '-';
404
                            continue;
405
                        }
406
 
407
                        p_dst[fmtlen-i] = cmd.pad;                                      // padding ' ' oder '0'
408
                    }                                                                   // end: if( dstcnt+fmtlen-i <= n )
409
                }
410
                p_dst  += fmtlen;
411
                dstcnt += fmtlen;
412
            }
413
            continue;
414
        }
415
 
416
        //------------------------
417
        // exec cmd: "s", "S", "c"
418
        //------------------------
419
        if( cmd.cmd == 's' || cmd.cmd == 'S' || cmd.cmd == 'c' )
420
        {
421
            switch(cmd.cmd)
422
            {
423
                case 's':   p_str = va_arg( ap, char *);                                // String aus dem RAM
424
                            break;
425
 
426
                case 'S':   strncpy_P( argbuff, va_arg( ap, char *), ARGBUFF_SIZE);     // String liegt im progmem -> in's RAM kopieren
427
                            argbuff[ARGBUFF_SIZE-1] = 0;
428
                            p_str = argbuff;
429
                            break;
430
 
431
                case 'c':   argbuff[0] = va_arg( ap, int);                              // einzelnes char
432
                            argbuff[1] = 0;
433
                            p_str = argbuff;
434
                            break;
435
            }
436
 
437
            fmtlen = cmd.prelen;
438
            arglen = strlen(p_str);
439
 
440
            if( !cmd.mask )                                     // keine Maske: alles vom String ausgeben
441
                fmtlen = arglen;
442
 
443
            for( i=0; i<fmtlen; i++)
444
            {
445
                if( dstcnt < n )                                // wenn Zielbuffer nicht ueberschritten
446
                {
447
                    if( *p_str )                                // char uebertragen
448
                    {
449
                        *p_dst = *p_str;
450
                        p_str++;
451
                    }
452
                    else                                        // padding
453
                    {
454
                        *p_dst = ' ';
455
                    }
456
                    p_dst++;
457
                }
458
                dstcnt++;
459
            }
460
 
461
            continue;
462
        }
463
 
464
        //------------------------
465
        // exec cmd: "%"
466
        //------------------------
467
        if( cmd.cmd == '%' )
468
        {
469
            *p_dst = '%';
470
            p_dst++;
471
            dstcnt++;
472
            continue;
473
        }
474
 
475
    } while( (dstcnt < n) && *p_fmt );
476
 
477
    *(p_dst + (dstcnt < n ? 0 : -1)) = 0;       // terminierende 0 im Ausgabebuffer setzen
478
}
479
 
480
 
481
 
482
//---------------------------------------------------------------------
483
//---------------------------------------------------------------------
484
void xsnprintf( char *buffer, uint8_t n, const char *format, ... )
485
{
486
    va_list ap;
487
 
488
    va_start(ap, format);
489
    _xvsnprintf( false, buffer, n, format, ap);
490
    va_end(ap);
491
}
492
 
493
 
494
//---------------------------------------------------------------------
495
//---------------------------------------------------------------------
496
void xsnprintf_P( char *buffer, uint8_t n, const char *format, ... )
497
{
498
    va_list ap;
499
 
500
    va_start(ap, format);
501
    _xvsnprintf( true, buffer, n, format, ap);
502
    va_end(ap);
503
}
504
 
505
 
506
 
507
//-----------------------------------------------------------
508
// buffered_sprintf_P( format, ...)
509
//
510
// Ausgabe direkt in einen internen Buffer.
511
// Der Pointer auf den RAM-Buffer wird zurueckgegeben.
512
// Abgesichert bzgl. Buffer-Overflow.
513
//
514
// Groesse des Buffers: PRINTF_BUFFER_SIZE
515
//
516
// Parameter:
517
//  format     : String aus PROGMEM (siehe: xprintf in utils/xstring.h)
518
//  ...        : Parameter fuer 'format'
519
//-----------------------------------------------------------
520
char * buffered_sprintf( const char *format, ... )
521
{
522
    va_list ap;
523
 
524
    va_start( ap, format );
525
    _xvsnprintf( false, sprintf_buffer, SPRINTF_BUFFER_SIZE, format, ap );
526
    va_end(ap);
527
    return sprintf_buffer;
528
}
529
 
530
 
531
 
532
//-----------------------------------------------------------
533
// buffered_sprintf_P( format, ...)
534
//
535
// Ausgabe direkt in einen internen Buffer.
536
// Der Pointer auf den RAM-Buffer wird zurueckgegeben.
537
// Abgesichert bzgl. Buffer-Overflow.
538
//
539
// Groesse des Buffers: PRINTF_BUFFER_SIZE
540
//
541
// Parameter:
542
//  format     : String aus PROGMEM (siehe: xprintf in utils/xstring.h)
543
//  ...        : Parameter fuer 'format'
544
//-----------------------------------------------------------
545
char * buffered_sprintf_P( const char *format, ... )
546
{
547
    va_list ap;
548
 
549
    va_start( ap, format );
550
    _xvsnprintf( true, sprintf_buffer, SPRINTF_BUFFER_SIZE, format, ap );
551
    va_end(ap);
552
    return sprintf_buffer;
553
}
554
 
555
 
556
 
557
//--------------------------------------------------------------
558
// kopiert einen String von src auf dst mit fester Laenge und
559
// ggf. Space paddings rechts
560
//
561
// - fuellt ggf. den dst-String auf size Laenge mit Spaces
562
// - setzt Terminierung's 0 bei dst auf Position size
563
//--------------------------------------------------------------
564
void strncpyfill( char *dst, const char *src, size_t size)
565
{
566
    uint8_t i;
567
    uint8_t pad = false;
568
 
569
    for( i=0; i<size; i++)
570
    {
571
        if(*src == 0) pad = true;
572
 
573
        if( pad )   *dst = ' ';
574
        else        *dst = *src;
575
 
576
        src++;
577
        dst++;
578
    }
579
    dst--;
580
    *dst = 0;
581
}
582
 
583
 
584
 
585
//--------------------------------------------------------------
586
// entfernt rechte Leerzeichen aus einem String
587
//--------------------------------------------------------------
588
void strrtrim( char *dst)
589
{
590
    char    *p;
591
 
592
    p = dst + strlen(dst) - 1;
593
 
594
    while( (p != dst) && (*p == ' ') ) p--;
595
 
596
    if( (*p != ' ') && (*p != 0) ) p++;
597
    *p = 0;
598
}
599
 
600
 
601
 
602
//--------------------------------------------------------------
603
// INTERN - fuer strncpyat(), strncpyat_P()
604
//--------------------------------------------------------------
605
void _strncpyat( char *dst, const char *src, size_t size, const char sepchar, uint8_t sepcharcount, uint8_t progmem)
606
{
607
    uint8_t i;
608
 
609
    if( progmem )
610
        strncpy_P( dst, src, size);
611
    else
612
        strncpy( dst, src, size);
613
 
614
    for( i=0; i<size; i++)
615
    {
616
        if( *dst == 0) return;
617
        if( *dst == sepchar)
618
        {
619
            sepcharcount--;
620
            if( sepcharcount==0 )
621
            {
622
                *dst = 0;
623
                return;
624
            }
625
        }
626
        dst++;
627
    }
628
    dst--;
629
    *dst = 0;
630
}
631
 
632
 
633
//--------------------------------------------------------------
634
// strncpyat( dst, src, size, sepchar)
635
//
636
// kopiert einen String von 'src 'auf 'dst' mit max. Laenge 'size'
637
// oder bis 'sepchar' gefunden wird.
638
//
639
// src in PROGMEM
640
//--------------------------------------------------------------
641
void strncpyat( char *dst, const char *src, size_t size, const char sepchar, uint8_t sepcharcount)
642
{
643
    _strncpyat( dst, src, size, sepchar, sepcharcount, false);
644
}
645
 
646
 
647
//--------------------------------------------------------------
648
// strncpyat_P( dst, src, size, sepchar)
649
//
650
// kopiert einen String von 'src 'auf 'dst' mit max. Laenge 'size'
651
// oder bis 'sepchar' gefunden wird.
652
//
653
// src in RAM
654
//--------------------------------------------------------------
655
void strncpyat_P( char *dst, const char *src, size_t size, const char sepchar, uint8_t sepcharcount)
656
{
657
    _strncpyat( dst, src, size, sepchar, sepcharcount, true);
658
}
659
 
660
 
661
 
662
//--------------------------------------------------------------
663
// UTCdatetime2local( PKTdatetime_t *dtbuffer, PKTdatetime_t dt )
664
//
665
// konvertiert die UTC-Time 'dt' in die lokale Zeit und speichert
666
// dieses in 'dtbuffer' ab.
667
//
668
// Parameter:
669
//
670
//  dtdst: Pointer Destination (PKTdatetime_t) (Speicher muss alloziiert sein!)
671
//  dtsrc: Pointer Source (PKTdatetime_t)
672
//
673
// Hinweise:
674
//
675
//  Schaltjahre (bzw. der 29.02.) werden nicht unterstuetzt
676
//--------------------------------------------------------------
677
void UTCdatetime2local( PKTdatetime_t *dtdst, PKTdatetime_t *dtsrc )
678
{
679
    int8_t  timeoffset;
680
    int32_t v;
681
    int8_t  diff;
682
    //                    01 02 03 04 05 06 07 08 09 10 11 12    Monat
683
    int8_t  daymonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
684
 
685
    //--------------------------
686
    // timezone: Einstellbereich -12 .. 0 .. 12 (+1 = Berlin)
687
    // summertime: Einstellung: 0 oder 1 (0=Winterzeit, 1=Sommerzeit)
688
    //--------------------------
689
    timeoffset = Config.timezone + Config.summertime;
690
    //timeoffset = 2;                                   // solange noch nicht in PKT-Config: Berlin, Sommerzeit
691
 
692
 
693
    memcpy( dtdst, dtsrc, sizeof(PKTdatetime_t) );      // copy datetime to destination
694
 
695
    //--------------------------
696
    // Zeitzonenanpassung
697
    //--------------------------
698
    if( dtdst->year != 0 && dtdst->month >= 1 && dtdst->month <= 12 )   // nur wenn gueltiges Datum vorhanden
699
    {
700
        //--------------------------
701
        // 1. Sekunden
702
        //--------------------------
703
        v = (int32_t)dtdst->seconds;
704
        v += timeoffset*3600;                           // Stunden korrigieren
705
        diff = 0;
706
 
707
        if( v > 86400 )                                 // Tagesueberschreitung?    (86400 = 24*60*60 bzw. 24 Stunden)
708
        {
709
            v -= 86400;
710
            diff++;                                     //  inc: Tag
711
        }
712
        else if( v < 0 )                                // Tagesunterschreitung?
713
        {
714
            v += 86400;
715
            diff--;                                     //  dec: Tag
716
        }
717
        dtdst->seconds = (uint32_t)v;                   // SET: seconds
718
 
719
        //--------------------------
720
        // 2. Tag
721
        //--------------------------
722
        v = (int32_t)dtdst->day;
723
        v += diff;
724
        diff = 0;
725
 
726
        if( v > daymonth[dtdst->month-1] )              // Monatsueberschreitung?
727
        {
728
            v = 1;                                      // erster Tag des Monats
729
            diff++;                                     //  inc: Monat
730
        }
731
        else if( v < 1 )                                // Monatsunterschreitung?
732
        {
733
            if( dtdst->month > 1 )
734
                v = daymonth[dtdst->month-1-1];         // letzter Tag des vorherigen Monats
735
            else
736
                v = 31;                                 // letzter Tag im Dezember des vorherigen Jahres
737
            diff--;                                     //   dec: Monat
738
        }
739
        dtdst->day = (uint8_t)v;                        // SET: day
740
 
741
        //--------------------------
742
        // 3. Monat
743
        //--------------------------
744
        v = (int32_t)dtdst->month;
745
        v += diff;
746
        diff = 0;
747
 
748
        if( v > 12 )                                    // Jahresueberschreitung?
749
        {
750
            v = 1;
751
            diff++;                                     //  inc: Jahr
752
        }
753
        else if( v < 1 )                                // Jahresunterschreitung?
754
        {
755
            v = 12;
756
            diff--;                                     //  dec: Jahr
757
        }
758
        dtdst->month = (uint8_t)v;                      // SET: month
759
 
760
        //--------------------------
761
        // 4. Jahr
762
        //--------------------------
763
        dtdst->year += diff;                            // SET: year
764
    }
765
}