Kernel/IPStack - (minor) TODO retransmit timer
[tpg/acess2.git] / KernelLand / Kernel / libc.c
1 /*
2  * Acess2 Kernel
3  * - By John Hodge (thePowersGang)
4  *
5  * libc.c
6  * - Kernel-land C Library
7  */
8 #include <acess.h>
9 #include <hal_proc.h>   // For MM_*
10 #include <utf16.h>
11
12 // === CONSTANTS ===
13 #define RANDOM_SEED     0xACE55052
14 #define RANDOM_A        0x00731ADE
15 #define RANDOM_C        12345
16 #define RANDOM_SPRUCE   0xf12b039
17
18 // === PROTOTYPES ===
19 #if 0
20 unsigned long long      strtoull(const char *str, char **end, int base);
21 unsigned long   strtoul(const char *str, char **end, int base);
22 signed long long        strtoll(const char *str, char **end, int base);
23 signed long     strtol(const char *str, char **end, int base);
24  int    atoi(const char *string);
25  int    ParseInt(const char *string, int *Val);
26 void    itoa(char *buf, Uint64 num, int base, int minLength, char pad);
27  int    vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args);
28  int    snprintf(char *__s, size_t __n, const char *__format, ...);
29  int    sprintf(char *__s, const char *__format, ...);
30  int    strucmp(const char *Str1, const char *Str2);
31 char    *strchr(const char *__s, int __c);
32  int    strpos(const char *Str, char Ch);
33 size_t  strlen(const char *__s);
34 char    *strcpy(char *__str1, const char *__str2);
35 char    *strncpy(char *__str1, const char *__str2, size_t max);
36 char    *strcat(char *dest, const char *source);
37  int    strcmp(const char *str1, const char *str2);
38  int    strncmp(const char *str1, const char *str2, size_t num);
39 char    *_strdup(const char *File, int Line, const char *Str);
40  int    rand(void);
41 void    *memmove(void *__dest, const void *__src, size_t len);
42
43  int    CheckString(char *String);
44  int    CheckMem(void *Mem, int NumBytes); 
45 #endif
46
47 // === EXPORTS ===
48 EXPORT(atoi);
49 EXPORT(itoa);
50 EXPORT(vsnprintf);
51 EXPORT(snprintf);
52 EXPORT(sprintf);
53 EXPORT(tolower);
54
55 EXPORT(strucmp);
56 EXPORT(strchr);
57 EXPORT(strrchr);
58 EXPORT(strpos);
59 EXPORT(strlen);
60 EXPORT(strcpy);
61 EXPORT(strncpy);
62 EXPORT(strcat);
63 EXPORT(strncat);
64 EXPORT(strcmp);
65 EXPORT(strncmp);
66 //EXPORT(strdup);
67 EXPORT(_strdup);        // Takes File/Line too
68 EXPORT(rand);
69 EXPORT(memmove);
70
71 EXPORT(CheckString);
72 EXPORT(CheckMem);
73
74 // === CODE ===
75 // - Import userland stroi.c file
76 #define _LIB_H_
77 #define _SysDebug(f,v...)       Log_Debug("libc", f ,## v)
78 #include "../../Usermode/Libraries/libc.so_src/strtoi.c"
79
80 int ParseInt(const char *string, int *Val)
81 {
82          int    ret = 0;
83          int    bNeg = 0;
84         const char *orig_string = string;
85         
86         //Log("atoi: (string='%s')", string);
87         
88         // Clear non-numeric characters
89         while( !('0' <= *string && *string <= '9') && *string != '-' )  string++;
90         if( *string == '-' ) {
91                 bNeg = 1;
92                 while( !('0' <= *string && *string <= '9') )    string++;
93         }
94         
95         if(*string == '0')
96         {
97                 string ++;
98                 if(*string == 'x')
99                 {
100                         // Hex
101                         string ++;
102                         for( ;; string ++ )
103                         {
104                                 if('0' <= *string && *string <= '9') {
105                                         ret *= 16;
106                                         ret += *string - '0';
107                                 }
108                                 else if('A' <= *string && *string <= 'F') {
109                                         ret *= 16;
110                                         ret += *string - 'A' + 10;
111                                 }
112                                 else if('a' <= *string && *string <= 'f') {
113                                         ret *= 16;
114                                         ret += *string - 'a' + 10;
115                                 }
116                                 else
117                                         break;
118                         }
119                 }
120                 else    // Octal
121                 {
122                         for( ; '0' <= *string && *string <= '7'; string ++ )
123                         {
124                                 ret *= 8;
125                                 ret += *string - '0';
126                         }
127                 }
128         }
129         else    // Decimal
130         {
131                 for( ; '0' <= *string && *string <= '9'; string++)
132                 {
133                         ret *= 10;
134                         ret += *string - '0';
135                 }
136                 // Error check
137                 if( ret == 0 )  return 0;
138         }
139         
140         if(bNeg)        ret = -ret;
141         
142         //Log("atoi: RETURN %i", ret);
143         
144         if(Val) *Val = ret;
145         
146         return string - orig_string;
147 }
148
149 static const char cUCDIGITS[] = "0123456789ABCDEF";
150 /**
151  * \fn void itoa(char *buf, Uint64 num, int base, int minLength, char pad)
152  * \brief Convert an integer into a character string
153  */
154 void itoa(char *buf, Uint64 num, int base, int minLength, char pad)
155 {
156         char    tmpBuf[64+1];
157          int    pos=0, i;
158         Uint64  rem;
159
160         buf[0] = 0;
161         ASSERTR(base >= 2, );
162         ASSERTR(base <= 16, );
163
164         // Sanity check
165         if(!buf)        return;
166         
167         // Convert 
168         while(num > base-1) {
169                 num = DivMod64U(num, base, &rem);       // Shift `num` and get remainder
170                 ASSERT(rem >= 0);
171                 if( rem >= base && base != 16 ) {
172                         Debug("rem(%llx) >= base(%x), num=%llx", rem, base, num);
173                 }
174                 ASSERT(rem < base);
175                 tmpBuf[pos] = cUCDIGITS[ rem ];
176                 pos++;
177         }
178         tmpBuf[pos++] = cUCDIGITS[ num ];               // Last digit of `num`
179         
180         // Put in reverse
181         i = 0;
182         minLength -= pos;
183         while(minLength-- > 0)  buf[i++] = pad;
184         while(pos-- > 0)                buf[i++] = tmpBuf[pos]; // Reverse the order of characters
185         buf[i] = 0;
186 }
187
188 /**
189  * \brief Append a character the the vsnprintf output
190  */
191 #define PUTCH(ch)       do { \
192                 if(pos < __maxlen && __s) { \
193                         __s[pos] = ch; \
194                 } else { \
195                         (void)ch;\
196                 } \
197                 pos ++; \
198         } while(0)
199 #define GETVAL()        do {\
200         if(isLongLong)  val = va_arg(args, Uint64);\
201         else    val = va_arg(args, unsigned int);\
202         }while(0)
203 /**
204  * \brief VArg String Number Print Formatted
205  */
206 int vsnprintf(char *__s, const size_t __maxlen, const char *__format, va_list args)
207 {
208         char    c, pad = ' ';
209          int    minSize = 0, precision = -1, len;
210         char    tmpBuf[34];     // For Integers
211         const char      *p = NULL;
212          int    isLongLong = 0, isLong;
213         Uint64  val;
214         size_t  pos = 0;
215         // Flags
216          int    bPadLeft = 0;
217
218         while((c = *__format++) != 0)
219         {
220                 // Non control character
221                 if(c != '%') { PUTCH(c); continue; }
222
223                 c = *__format++;
224                 if(c == '\0')   break;
225                 
226                 // Literal %
227                 if(c == '%') { PUTCH('%'); continue; }
228                 
229                 // Pointer - Done first for debugging
230                 if(c == 'p') {
231                         Uint    ptr = va_arg(args, Uint);
232                         PUTCH('*');     PUTCH('0');     PUTCH('x');
233                         for( len = BITS/4; len -- && ((ptr>>(len*4))&15) == 0; )
234                                 ;
235                         len ++;
236                         if( len == 0 )
237                                 PUTCH( '0' );
238                         else
239                                 while( len -- )
240                                         PUTCH( cUCDIGITS[ (ptr>>(len*4))&15 ] );
241                         continue ;
242                 }
243
244                 isLongLong = 0;
245                 isLong = 0;
246         
247                 // - Padding Side Flag
248                 if(c == '-') {
249                         bPadLeft = 1;
250                         c = *__format++;
251                 }
252                 
253                 // - Padding
254                 if(c == '0') {
255                         pad = '0';
256                         c = *__format++;
257                 }
258                 else
259                         pad = ' ';
260                 
261                 // - Minimum length
262                 if(c == '*') {  // Dynamic length
263                         minSize = va_arg(args, unsigned int);
264                         c = *__format++;
265                 }
266                 else if('1' <= c && c <= '9')
267                 {
268                         minSize = 0;
269                         while('0' <= c && c <= '9')
270                         {
271                                 minSize *= 10;
272                                 minSize += c - '0';
273                                 c = *__format++;
274                         }
275                 }
276                 else
277                         minSize = 0;
278                 
279                 // - Precision
280                 precision = -1;
281                 if( c == '.' ) {
282                         c = *__format++;
283                         
284                         if(c == '*') {  // Dynamic length
285                                 precision = va_arg(args, unsigned int);
286                                 c = *__format++;
287                         }
288                         else if('1' <= c && c <= '9')
289                         {
290                                 precision = 0;
291                                 while('0' <= c && c <= '9')
292                                 {
293                                         precision *= 10;
294                                         precision += c - '0';
295                                         c = *__format++;
296                                 }
297                         }
298                 }
299                 
300                 // - Default, Long or LongLong?
301                 isLongLong = 0;
302                 if(c == 'l')    // Long is actually the default on x86
303                 {
304                         isLong = 1;
305                         c = *__format++;
306                         if(c == 'l') {
307                                 c = *__format++;
308                                 isLongLong = 1;
309                         }
310                 }
311                 
312                 // - Now get the format code
313                 p = tmpBuf;
314                 switch(c)
315                 {
316                 case 'd':
317                 case 'i':
318                         GETVAL();
319                         if( isLongLong && val >> 63 ) {
320                                 PUTCH('-');
321                                 if( val == LLONG_MIN )
322                                         val = LLONG_MAX;
323                                 else
324                                         val = -val;
325                         }
326                         else if( !isLongLong && (val >> 31) ) {
327                                 PUTCH('-');
328                                 val = (~val & 0xFFFFFFFF)+1;
329                         }
330                         itoa(tmpBuf, val, 10, minSize, pad);
331                         goto printString;
332                 case 'u':       // Unsigned
333                         GETVAL();
334                         itoa(tmpBuf, val, 10, minSize, pad);
335                         goto printString;
336                 case 'P':       // Physical Address
337                         PUTCH('0');
338                         PUTCH('x');
339                         if(sizeof(tPAddr) > 4)  isLongLong = 1;
340                         GETVAL();
341                         itoa(tmpBuf, val, 16, minSize, pad);
342                         goto printString;
343                 case 'X':       // Hex
344                         if(BITS == 64)
345                                 isLongLong = 1; // TODO: Handle non-x86 64-bit archs
346                         GETVAL();
347                         itoa(tmpBuf, val, 16, minSize, pad);
348                         goto printString;
349                         
350                 case 'x':       // Lower case hex
351                         GETVAL();
352                         itoa(tmpBuf, val, 16, minSize, pad);
353                         goto printString;
354                 case 'o':       // Octal
355                         GETVAL();
356                         itoa(tmpBuf, val, 8, minSize, pad);
357                         goto printString;
358                 case 'b':
359                         GETVAL();
360                         itoa(tmpBuf, val, 2, minSize, pad);
361                         goto printString;
362
363                 case 'B':       //Boolean
364                         val = va_arg(args, unsigned int);
365                         if(val) p = "True";
366                         else    p = "False";
367                         goto printString;
368                 
369                 // String - Null Terminated Array
370                 case 's':
371                         if( isLong ) {
372                                 Uint16  *p16 = va_arg(args, Uint16*);
373                                 Uint8   tmp[5];
374                                 while( *p16 && precision-- ) {
375                                         Uint32  cp;
376                                         p16 += ReadUTF16(p16, &cp);
377                                         tmp[WriteUTF8(tmp, cp)] = 0;
378                                         for(int i = 0; tmp[i] && i<5; i ++)
379                                                 PUTCH(tmp[i]);
380                                 }
381                                 break;
382                         }
383                         p = va_arg(args, char*);        // Get Argument
384                         if( p && !CheckString(p) )      goto invalString;       // Avoid #PFs  
385                 printString:
386                         if(!p)          p = "(null)";
387                         len = strlen(p);
388                         if( !bPadLeft ) while(len++ < minSize)  PUTCH(pad);
389                         while(*p && precision--) { PUTCH(*p); p++;} 
390                         if( bPadLeft )  while(len++ < minSize)  PUTCH(pad);
391                         break;
392                 invalString:    
393                         PUTCH('(');PUTCH('i');PUTCH('n');PUTCH('v');PUTCH('a'); PUTCH('l');PUTCH(':');
394                         PUTCH('*');PUTCH('0');PUTCH('x');
395                         val = (tVAddr)p;
396                         for( len = BITS/4; len -- && ((val>>(len*4))&15) == 0; )
397                                 ;
398                         len ++;
399                         if( len == 0 )
400                                 PUTCH( '0' );
401                         else
402                                 while( len -- )
403                                         PUTCH( cUCDIGITS[ (val>>(len*4))&15 ] );
404                         PUTCH(')');
405                         break;
406                 
407                 case 'C':       // Non-Null Terminated Character Array
408                         p = va_arg(args, char*);
409                         if( !CheckMem(p, minSize) ) {
410                                 p = "(inval)";
411                                 goto printString;
412                         }
413                         if(!p)  goto printString;
414                         while(minSize--) {
415                                 if(*p == '\0') {
416                                         PUTCH('\\');
417                                         PUTCH('0');
418                                 }
419                                 else if(*p == '\\') {
420                                         PUTCH('\\');
421                                         PUTCH('\\');
422                                 }
423                                 else {
424                                         PUTCH(*p);
425                                 }
426                                 p ++;
427                         }
428                         break;
429                 
430                 // Single Character
431                 case 'c':
432                 default:
433                         GETVAL();
434                         PUTCH( (Uint8)val );
435                         break;
436                 }
437         }
438         
439         if(__s && pos < __maxlen)
440                 __s[pos] = '\0';
441         
442         return pos;
443 }
444 #undef PUTCH
445
446 /**
447  */
448 int snprintf(char *__s, size_t __n, const char *__format, ...)
449 {
450         va_list args;
451          int    ret;
452         
453         va_start(args, __format);
454         ret = vsnprintf(__s, __n, __format, args);
455         va_end(args);
456         
457         return ret;
458 }
459
460 /**
461  */
462 int sprintf(char *__s, const char *__format, ...)
463 {
464         va_list args;
465          int    ret;
466         
467         va_start(args, __format);
468         ret = vsnprintf(__s, -1, __format, args);
469         va_end(args);
470         
471         return ret;
472 }
473
474 /*
475  * ==================
476  * ctype.h
477  * ==================
478  */
479 int isalnum(int c)
480 {
481         return isalpha(c) || isdigit(c);
482 }
483 int isalpha(int c)
484 {
485         return isupper(c) || islower(c);
486 }
487 int isascii(int c)
488 {
489         return (0 <= c && c < 128);
490 }
491 int isblank(int c)
492 {
493         if(c == '\t')   return 1;
494         if(c == ' ')    return 1;
495         return 0;
496 }
497 int iscntrl(int c)
498 {
499         // TODO: Check iscntrl
500         if(c < ' ')     return 1;
501         return 0;
502 }
503 int isdigit(int c)
504 {
505         return ('0' <= c && c <= '9');
506 }
507 int isgraph(int c)
508 {
509         // TODO: Check isgraph
510         return 0;
511 }
512 int islower(int c)
513 {
514         return ('a' <= c && c <= 'z');
515 }
516 int isprint(int c)
517 {
518         if( ' ' <= c && c <= 0x7F )     return 1;
519         return 0;
520 }
521 int ispunct(int c)
522 {
523         switch(c)
524         {
525         case '.':       case ',':
526         case '?':       case '!':
527                 return 1;
528         default:
529                 return 0;
530         }
531 }
532 int isspace(int c)
533 {
534         if(c == ' ')    return 1;
535         if(c == '\t')   return 1;
536         if(c == '\v')   return 1;
537         if(c == '\n')   return 1;
538         if(c == '\r')   return 1;
539         return 0;
540 }
541 int isupper(int c)
542 {
543         return ('A' <= c && c <= 'Z');
544 }
545 int isxdigit(int c)
546 {
547         return isdigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
548 }
549
550 int toupper(int c)
551 {
552         if( islower(c) )
553                 return c - 0x20;
554         else
555                 return c;
556 }
557 int tolower(int c)
558 {
559         if( isupper(c) )
560                 return c + 0x20;
561         else
562                 return c;
563 }
564
565 /**
566  * \fn int strucmp(const char *Str1, const char *Str2)
567  * \brief Compare \a Str1 and \a Str2 case-insensitively
568  */
569 int strucmp(const char *Str1, const char *Str2)
570 {
571         while(*Str1 && tolower(*Str1) == tolower(*Str2))
572                 Str1++, Str2++;
573         return tolower(*Str1) - tolower(*Str2);
574 }
575
576 /**
577  * \brief Locate a byte in a string
578  */
579 char *strchr(const char *__s, int __c)
580 {
581         for( ; *__s; __s ++ )
582         {
583                 if( *__s == __c )       return (char*)__s;
584         }
585         return NULL;
586 }
587
588 char *strrchr(const char *__s, int __c)
589 {
590         size_t ofs = strlen(__s);
591         while(--ofs && __s[ofs] != __c);
592         if( __s[ofs] == __c )
593                 return (char*)__s + ofs;
594         return NULL;
595 }
596
597 /**
598  * \fn int strpos(const char *Str, char Ch)
599  * \brief Search a string for an ascii character
600  */
601 int strpos(const char *Str, char Ch)
602 {
603          int    pos;
604         for(pos=0;Str[pos];pos++)
605         {
606                 if(Str[pos] == Ch)      return pos;
607         }
608         return -1;
609 }
610
611 /**
612  * \fn size_t strlen(const char *__str)
613  * \brief Get the length of string
614  */
615 size_t strlen(const char *__str)
616 {
617         size_t  ret = 0;
618         while(*__str++) ret++;
619         return ret;
620 }
621
622 /**
623  * \brief Copy a string to a new location
624  */
625 char *strcpy(char *__str1, const char *__str2)
626 {
627         while(*__str2)
628                 *__str1++ = *__str2++;
629         *__str1 = '\0'; // Terminate String
630         return __str1;
631 }
632
633 /**
634  * \brief Copy a string to a new location
635  * \note Copies at most `max` chars
636  */
637 char *strncpy(char *__str1, const char *__str2, size_t __max)
638 {
639         while(*__str2 && __max-- >= 1)
640                 *__str1++ = *__str2++;
641         if(__max)
642                 *__str1 = '\0'; // Terminate String
643         return __str1;
644 }
645
646 /**
647  * \brief Append a string to another
648  */
649 char *strcat(char *__dest, const char *__src)
650 {
651         while(*__dest++);
652         __dest--;
653         while(*__src)
654                 *__dest++ = *__src++;
655         *__dest = '\0';
656         return __dest;
657 }
658
659 /**
660  * \brief Append at most \a n chars to a string from another
661  * \note At most n+1 chars are written (the dest is always zero terminated)
662  */
663 char *strncat(char *__dest, const char *__src, size_t n)
664 {
665         while(*__dest++);
666         while(*__src && n-- >= 1)
667                 *__dest++ = *__src++;
668         *__dest = '\0';
669         return __dest;
670 }
671
672 /**
673  * \fn int strcmp(const char *str1, const char *str2)
674  * \brief Compare two strings return the difference between
675  *        the first non-matching characters.
676  */
677 int strcmp(const char *str1, const char *str2)
678 {
679         while(*str1 && *str1 == *str2)
680                 str1++, str2++;
681         return *str1 - *str2;
682 }
683
684 /**
685  * \fn int strncmp(const char *Str1, const char *Str2, size_t num)
686  * \brief Compare strings \a Str1 and \a Str2 to a maximum of \a num characters
687  */
688 int strncmp(const char *Str1, const char *Str2, size_t num)
689 {
690         if(num == 0)    return 0;       // TODO: Check what should officially happen here
691         while(--num && *Str1 && *Str1 == *Str2)
692                 Str1++, Str2++;
693         return *Str1-*Str2;
694 }
695
696 #if 0
697 /**
698  * \fn char *strdup(const char *Str)
699  * \brief Duplicates a string
700  */
701 char *strdup(const char *Str)
702 {
703         char    *ret;
704         ret = malloc(strlen(Str)+1);
705         if( !ret )      return NULL;
706         strcpy(ret, Str);
707         return ret;
708 }
709 #else
710
711 /**
712  * \fn char *_strdup(const char *File, int Line, const char *Str)
713  * \brief Duplicates a string
714  */
715 char *_strdup(const char *File, int Line, const char *Str)
716 {
717         char    *ret;
718         ret = Heap_Allocate(File, Line, strlen(Str)+1);
719         if( !ret )      return NULL;
720         strcpy(ret, Str);
721         return ret;
722 }
723 #endif
724
725 /**
726  * \fn int rand()
727  * \brief Pseudo random number generator
728  */
729 int rand(void)
730 {
731         #if 0
732         static Uint     state = RANDOM_SEED;
733         Uint    old = state;
734         // Get the next state value
735         giRandomState = (RANDOM_A*state + RANDOM_C);
736         // Check if it has changed, and if it hasn't, change it
737         if(state == old)        state += RANDOM_SPRUCE;
738         return state;
739         #else
740         // http://en.wikipedia.org/wiki/Xorshift
741         // 2010-10-03
742         static Uint32   x = 123456789;
743         static Uint32   y = 362436069;
744         static Uint32   z = 521288629;
745         static Uint32   w = 88675123; 
746         Uint32  t;
747  
748         t = x ^ (x << 11);
749         x = y; y = z; z = w;
750         return w = w ^ (w >> 19) ^ t ^ (t >> 8); 
751         #endif
752 }
753
754 void *memmove(void *__dest, const void *__src, size_t len)
755 {
756         char    *dest = __dest;
757         const char      *src = __src;
758         void    *ret = __dest;
759
760         if( len == 0 || dest == src )
761                 return dest;
762         
763         if( (tVAddr)dest > (tVAddr)src + len )
764                 return memcpy(dest, src, len);
765         if( (tVAddr)dest + len < (tVAddr)src )
766                 return memcpy(dest, src, len);
767         
768         // NOTE: Assumes memcpy works forward
769         if( (tVAddr)dest < (tVAddr)src )
770                 return memcpy(dest, src, len);
771
772         #if 0
773         size_t  block_size;
774         if( (tVAddr)dest < (tVAddr)src )
775                 block_size = (tVAddr)src - (tVAddr)dest;
776         else
777                 block_size = (tVAddr)dest - (tVAddr)src;
778         
779         block_size &= ~0xF;
780         
781         while(len >= block_size)
782         {
783                 memcpy(dest, src, block_size);
784                 len -= block_size;
785                 dest += block_size;
786                 src += block_size;
787         }
788         memcpy(dest, src, len);
789         return ret;
790         #else
791         for( int i = len; i--; )
792         {
793                 dest[i] = src[i];
794         }
795         return ret;
796         #endif
797         
798 }
799
800 // NOTE: Strictly not libc, but lib.c is used by userland code too and hence these two
801 // can't be in it.
802 /**
803  * \name Memory Validation
804  * \{
805  */
806 /**
807  * \brief Checks if a string resides fully in valid memory
808  */
809 int CheckString(const char *String)
810 {
811         tVAddr  addr;
812          int    bUser;
813
814         addr = (tVAddr)String;
815
816         if( !MM_GetPhysAddr( (void*)addr ) )
817                 return 0;
818         
819         // Check 1st page
820         bUser = MM_IsUser( addr );
821         
822         while( *(char*)addr )
823         {
824                 if( (addr & (PAGE_SIZE-1)) == 0 )
825                 {
826                         if(bUser && !MM_IsUser(addr) )
827                                 return 0;
828                         if(!bUser && !MM_GetPhysAddr((void*)addr) )
829                                 return 0;
830                 }
831                 addr ++;
832         }
833         return 1;
834 }
835
836 /**
837  * \brief Check if a sized memory region is valid memory
838  * \return Boolean success
839  */
840 int CheckMem(const void *Mem, int NumBytes)
841 {
842         return MM_IsValidBuffer( (tVAddr)Mem, NumBytes );
843 }
844 /* *
845  * \}
846  */
847

UCC git Repository :: git.ucc.asn.au