Initial git commit -- Transition from CodeGuard repository
[sdk] / ecere / src / com / String.ec
1 namespace sys;
2
3 #define uint _uint
4
5 default:
6
7 #include <stdlib.h>
8 #if !defined(ECERE_BOOTSTRAP) // quick fix for now
9 #if defined(__WIN32__)
10 #define WIN32_LEAN_AND_MEAN
11 #include <windows.h>
12 #else
13 #include <unistd.h>
14 #endif
15 #endif
16
17 #undef uint
18 private:
19
20 import "instance"
21
22 default extern Platform runtimePlatform;
23
24 #define IS_ALUNDER(ch) ((ch) == '_' || isalnum((ch)))
25
26 public define DIR_SEP   = (GetRuntimePlatform() == win32) ? '\\' : '/';
27 public define DIR_SEPS  = (GetRuntimePlatform() == win32) ? "\\" : "/";
28
29 // Maximum length for a vsprintf string
30 public define MAX_F_STRING = 1025;
31
32 // Maximum length for a directories and filenames strings
33 public define MAX_EXTENSION = 17;  // 16 + \0
34 public define MAX_FILENAME = 274; // 256 chars + extension + dot + \0
35 public define MAX_DIRECTORY = 534; // 8 levels + 8 separators + \0
36 public define MAX_LOCATION = 797; // directory + filename + separator + \0
37
38 // --- File related String functions ---
39 public char * GetExtension(char * string, char * output)
40 {
41    int c;
42    int len = strlen(string);
43    int limit = Max(0, len-MAX_EXTENSION);
44    output[0] = '\0';
45    for(c = len; c>=limit; c--)
46    {
47       char ch = string[c];
48       if(ch == '.')
49       {
50          strcpy(output, string+c+1);
51          break;
52       }
53       else if(ch == '/' || ch == '\\')
54          break;
55    }
56    return output;
57 }
58
59 public char * StripLastDirectory(char * string, char * output)
60 {
61    int c;
62    if(runtimePlatform == win32 && !strcmp(string, "\\\\"))
63    {
64       strcpy(output, "/");
65       return output;
66    }
67    else
68    {
69       int len = strlen(string);
70       for(c = len-2; c>=0; c--)
71          if(string[c] == '/' || string[c] == '\\')
72             break;
73          else if(string[c] == '>' || (string[c] == ':' && c == 0))
74          {
75             c++;
76             break;
77          }
78
79       if((runtimePlatform == win32) ? (c >= 0) : (c > 0))
80       {
81          strncpy(output, string, c);
82          if(c > 0)
83          {
84             if(runtimePlatform == win32 && c == 1 && output[0] == '\\' && output[1] == '\\')
85                output[2] = '\0';
86             else
87                output[c] = '\0';
88          }
89          else
90             strcpy(output, DIR_SEPS);
91          return output;
92       }
93       else
94       {
95          // Return root on UNIX instead of null...
96          if(c == 0)
97          {
98             strcpy(output, DIR_SEPS);
99             return output;
100          }
101          else
102          {
103             strcpy(output, "");
104             return null;
105          }
106       }
107    }
108 }
109
110 public char * SplitDirectory(const char * string, char * part, char * rest)
111 {
112    int len = 0;
113    char ch;
114    int c = 0;
115    for(;(ch = string[c]) && (ch == '/' || ch == '\\'); c++);
116
117    if(c)
118       part[len++] = DIR_SEP;
119    else
120    {
121       for(;(ch = string[c]) && (ch != '/' && ch != '\\'); c++)
122       {
123          if(len < MAX_FILENAME)
124             part[len++] = ch;  
125       }
126    }
127
128    for(;(ch = string[c]) && (ch == '/' || ch == '\\'); c++);
129    strcpy(rest, string + c);
130    for(c = strlen(rest); c >= 0; c--)
131       if(ch != '/' && ch != '\\') 
132          break;
133    if(c > 0)
134       rest[c] = '\0';
135
136    part[len] = '\0';
137    return rest;
138 }
139
140 public char * GetLastDirectory(char * string, char * output)
141 {
142    int c;
143    int len = string ? strlen(string) : 0;
144    for(c = len-2; c>=0; c--)
145       if(string[c] == '/' || string[c] == '\\' || string[c] == ':' || string[c] == '>')
146          break;
147
148    c++;
149    if(c >= 0)
150       strcpy(output, string+c);
151    else
152       output[0] = '\0';
153
154    len = strlen(output);
155    if(len > 1 && (output[len-1] == '\\' || output[len-1] == '/'))
156       output[len-1] = '\0';   
157    return output;
158 }
159
160 public bool SplitArchivePath(char * fileName, char * archiveName, char ** archiveFile)
161 {
162    // Support Archives
163    if(fileName[0] == '<')
164    {
165       int c = strlen(fileName);
166       for(;c>0 && fileName[c] != '>'; c--);
167       if(c > 0)
168       {
169          strncpy(archiveName, fileName + 1, c - 1);
170          archiveName[c - 1] = '\0';
171          *archiveFile = fileName + c + 1;
172          return true;
173       }
174    }
175    else if(fileName[0] == ':')
176    {
177       strcpy(archiveName, ":");
178       *archiveFile = fileName + 1;
179       return true;
180    }
181    return false;
182 }
183
184 public char * PathCat(char * string, char * addedPath)
185 {
186    bool modified = false;
187    if(addedPath)
188    {
189       char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "", * file;
190       int c = 0;
191       bool isURL = false;
192       char * urlFileName;
193
194       if(SplitArchivePath(string, archiveName, &file))
195          strcpy(fileName, file);
196       else
197       {
198          strcpy(fileName, string);
199       }
200
201       if(strstr(string, "http://") == string)
202       {
203          char * slash = strstr(fileName + 7, "/");
204          isURL = true;
205          if(slash)
206             urlFileName = slash;
207          else
208             urlFileName = fileName + strlen(fileName);
209       }
210       if(strstr(addedPath, "http://") == addedPath)
211       {
212          strcpy(fileName, "http://");
213          isURL = true;
214          c = 7;
215       }
216       else if(runtimePlatform == win32)
217       {
218          if(addedPath[0] && addedPath[1] == ':' && addedPath[0] != '<')
219          {
220             fileName[0] = (char)toupper(addedPath[0]);
221             fileName[1] = ':';
222             fileName[2] = '\0';
223             c = 2;
224             modified = true;
225          }
226          else if(addedPath[0] == '\\' && addedPath[1] == '\\')
227          {
228             fileName[0] = fileName[1] = '\\';
229             fileName[2] = '\0';
230             c = 2;
231             modified = true;
232          }
233          // A drive needs to be selected
234          else if(fileName[0] == '/' && !archiveName[0] && strcmp(addedPath, "/"))
235             return null;
236       }
237
238       if(!modified && isURL && (addedPath[0] == '\\' || addedPath[0] == '/'))
239       {
240          urlFileName[0] = '/';
241          urlFileName[1] = '\0';
242       }
243       else if(!modified && (addedPath[0] == '\\' || addedPath[0] == '/'))
244       {
245          if(runtimePlatform == win32)
246          {
247             // Entire Computer
248             if(addedPath[0] == '/' && !addedPath[1])
249             {
250                fileName[0] = addedPath[0];
251                fileName[1] = '\0';
252                modified = true;
253             }
254             // Root of drive
255             else if(fileName[0] && fileName[1] == ':')
256             {
257                fileName[2] = '\0';
258                modified = true;
259             }
260             // Relative path root of drive
261             else
262             {
263                fileName[0] = '\\';
264                fileName[1] = '\0';
265                modified = true;
266             }
267          }
268          else
269          {
270             fileName[0] = '/';
271             fileName[1] = '\0';
272             modified = true;
273          }
274          c = 1;
275       }
276
277       for(; addedPath[c]; )
278       {
279          // DANGER OF OVERFLOW HERE
280          // char directory[MAX_FILENAME];
281          char directory[MAX_FILENAME * 16];
282          int len = 0;
283          char ch;
284          int count;
285       
286          for(;(ch = addedPath[c]) && (ch == '/' || ch == '\\'); c++);
287          for(;(ch = addedPath[c]) && (ch != '/' && ch != '\\'); c++)
288          {
289             if(isURL && ch == '?')
290             {
291                break;
292             }
293             if(len < MAX_FILENAME)
294                directory[len++] = ch;  
295          }
296          directory[len] = '\0';
297
298          // Trim rightmost spaces
299          for(count = len-1; count >= 0 && (directory[count] == ' ' || directory[count] == '\t'); count--)
300          {
301             directory[count] = '\0';
302             len--;
303          }
304
305          if(len > 0)
306          {
307             modified = true;
308             if(strstr(directory, "..") == directory && (!directory[2] || directory[2] == DIR_SEP))
309             {
310                int strLen = strlen(fileName) - 1;
311                if(strLen > -1)
312                {
313                   bool separator = false;
314
315                   // Go back one directory
316                   for(;(ch = fileName[strLen]) && strLen > -1 && (ch == '/' || ch == '\\'); strLen--);
317                   for(;(ch = fileName[strLen]) && strLen > -1 && (ch != '/' && ch != '\\' && ch != ':'); strLen--);
318                   for(;(ch = fileName[strLen]) && strLen > -1 && (ch == '/' || ch == '\\'); strLen--) separator = true;
319
320                   if(isURL)
321                   {
322                      strLen = Max(strLen, urlFileName - fileName);
323                   }
324                   if(!strcmp(fileName + strLen + (separator ? 2 : 1), ".."))
325                   {
326                      strcat(fileName, DIR_SEPS);
327                      strcat(fileName, "..");
328                   }
329                   else
330                   {
331                      if(runtimePlatform == win32)
332                      {
333                         if(!strLen && fileName[0] == '\\' && fileName[1] == '\\')
334                         {
335                            if(!fileName[2])
336                               return null;
337                            else
338                            {
339                               fileName[0] = '\\';
340                               fileName[1] = '\\';
341                               fileName[2] = '\0';
342                            }
343                         }
344                         else
345                            fileName[strLen+1] = '\0';
346                      }
347                      else
348                      {
349                         fileName[strLen+1] = '\0';
350                         if(strLen<0)
351                         {
352                            fileName[0] = '/';
353                            fileName[1] = '\0';
354                            strLen = 2;
355                         }
356                      }
357                   }
358                }
359                else
360                {
361                   strcpy(fileName, "..");
362                }
363             }
364             else if(strcmp(directory, "."))
365             {
366                int strLen = strlen(fileName);
367                bool notZeroLen = strLen > 0;
368                // if(strLen > 1 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
369                if(strLen > 0 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
370                   strLen--;
371                if(notZeroLen /*&& fileName[strLen-1] != ':' && fileName[strLen-1] != '>'*/)
372                {
373                   if(isURL)
374                      fileName[strLen++] = '/';
375                   else
376                      fileName[strLen++] = DIR_SEP;
377                }
378
379                fileName[strLen] = '\0';
380
381                if(strLen + strlen(directory) > MAX_LOCATION - 3)
382                   return null;   // AN ERROR OCCURED!
383
384                strcat(fileName, directory);
385             }
386          }
387          if(isURL && ch == '/')
388             strcat(fileName, "/");
389          if(isURL && ch == '?')
390          {
391             strcat(fileName, addedPath+c);
392             break;
393          }
394       }
395       if(archiveName[0])
396          sprintf(string, "<%s>%s", archiveName, fileName);
397       else
398          strcpy(string, fileName);
399    }
400    return modified ? string : null;
401 }
402
403 public char * MakePathRelative(char * path, char * to, char * destination)
404 {
405    // Don't process empty paths
406    if(!path[0])
407       strcpy(destination, path);
408    else
409    {
410       // TOFIX: DANGER OF OVERFLOW HERE
411       char pathPart[MAX_FILENAME * 16], pathRest[MAX_LOCATION];
412       char toPart[MAX_FILENAME * 16], toRest[MAX_LOCATION];
413       bool different = false;
414
415       strcpy(pathRest, path);
416       strcpy(toRest, to);
417
418       destination[0] = '\0';
419       for(;toRest[0];)
420       {
421          SplitDirectory(toRest, toPart, toRest);      
422          if(!different)
423             SplitDirectory(pathRest, pathPart, pathRest);
424
425          if(different || fstrcmp(toPart, pathPart))
426          {
427             different = true;
428             strcat(destination, "..");
429             strcat(destination, DIR_SEPS);
430          }
431       }
432
433       if(different)
434          PathCat(destination, pathPart);
435       for(;pathRest[0];)
436       {
437          SplitDirectory(pathRest, pathPart, pathRest);
438          PathCat(destination, pathPart);
439       }
440    }
441    return destination;
442 }
443
444 public bool StripExtension(char * string)
445 {
446    int c;
447    for(c = strlen(string); c>=0; c--)
448       if(string[c] == '.')
449       {
450          string[c] = '\0';
451          return true;
452       }
453       else if(string[c] == '\\' || string[c] == '/')
454          break;
455    return false;
456 }
457
458 public char * ChangeExtension(char * string, char * ext, char * output)
459 {
460    strcpy(output, string);
461    StripExtension(output);
462    if(ext[0])
463       strcat(output, ".");
464    strcat(output, ext);
465    return output;
466 }
467
468 // --- String Stuff (Temporarily outside String class) ---
469 public void PrintSize(char * string, uint size, int prec)
470 {
471    if(size > 1024)
472    {
473       char format[8];
474       sprintf(format, "%%.0%df", prec);
475       if(size > 1024 * 1024 * 1024)
476       {
477          sprintf(string, format, size / (float)(1024 * 1024 * 1024));
478          strcat(string, " GB");
479       }
480       else if(size > 1024 * 1024)
481       {
482          sprintf(string, format, size / (float)(1024 * 1024));
483          strcat(string, " MB");
484       }
485       else
486       {
487          sprintf(string, format, size / (float)1024);
488          strcat(string, " KB");
489       }
490    }
491    else
492       sprintf(string, "%d B", size);
493 }
494
495 public void PrintBigSize(char * string, double size, int prec)
496 {
497    if(size > 1024)
498    {
499       char format[8];
500       sprintf(format, "%%.0%df", prec);
501       if(size > 1024.0 * 1024.0 * 1024.0 * 1024.0)
502       {
503          sprintf(string, format, size / (1024 * 1024 * 1024.0 * 1024.0));
504          strcat(string, " TB");
505       }
506       else if(size > 1024.0 * 1024.0 * 1024.0)
507       {
508          sprintf(string, format, size / (1024.0 * 1024.0 * 1024.0));
509          strcat(string, " GB");
510       }
511       else if(size > 1024.0 * 1024.0)
512       {
513          sprintf(string, format, size / (1024.0 * 1024.0));
514          strcat(string, " MB");
515       }
516       else
517       {
518          sprintf(string, format, size / 1024.0);
519          strcat(string, " KB");
520       }
521    }
522    else
523       sprintf(string, "%.0f B", size);
524 }
525
526 public char * SearchString(char * buffer, int start, char * subStr, bool matchCase, bool matchWord)
527 {
528    if(buffer && subStr)
529    {
530       char * ptr;
531       char * strBuffer = buffer + start;
532       int subLen = strlen(subStr);
533       char beforeChar = start ? *(strBuffer-1) : 0;
534       int (*strcompare)(const char *, const char *, unsigned int) = matchCase ? strncmp : strnicmp;
535
536       for(ptr = strBuffer; *ptr; ptr++)
537       {
538          if(matchCase ? (*subStr == *ptr) : (tolower(*subStr) == tolower(*ptr)))
539          {
540             if(matchWord)
541             {
542                if(!strcompare(ptr,subStr,subLen) && 
543                   /*
544                   !IS_ALUNDER(ptr[subLen]) && 
545                   !IS_ALUNDER(beforeChar))
546                   */
547                   (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr[subLen])) && 
548                   (!IS_ALUNDER(subStr[0]) || !IS_ALUNDER(beforeChar)))
549                   return ptr;
550             }
551             else
552             {
553                if(!strcompare(ptr,subStr,subLen))
554                   return ptr;
555             }
556          }
557          beforeChar = ptr[0];  
558       }
559    }
560    return null;
561 }
562
563 public char * RSearchString(char * buffer, char * subStr, int maxLen, bool matchCase, bool matchWord)
564 {
565    if(buffer && subStr)
566    {
567       int subLen = strlen(subStr);
568       char * ptr1 = buffer + maxLen - subLen;
569       char * ptr2 = buffer + maxLen - subLen - 1;
570       int (*strcompare)(const char *, const char *, unsigned int) = matchCase ? strncmp : strnicmp;
571       for(; ptr1 >=buffer; ptr1--, ptr2--)
572       {
573          if(tolower(*subStr) == tolower(*ptr1))
574          {
575             if(matchWord)
576             {
577                if(!strcompare(ptr1,subStr,subLen) && 
578                   //!IS_ALUNDER(ptr1[subLen]) && !IS_ALUNDER(*ptr2))
579                   (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr1[subLen])) && 
580                   (!IS_ALUNDER(subStr[0]) || !IS_ALUNDER(*ptr2)))
581
582                  return ptr1;
583             }
584             else
585             {
586                if(!strcompare(ptr1,subStr,subLen))
587                   return ptr1;
588             }
589          }
590       }
591    }
592    return null;
593 }
594
595 public int Tokenize(char * string, int maxTokens, char* tokens[], bool escapeBackSlashes)
596 {
597    int count = 0;
598    bool quoted = false;
599    byte * start = null;
600    bool escaped = false;
601    char * output = string;
602
603    for(; *string && count < maxTokens; string++, output++)
604    {
605       if(output != string)
606          *output = *string;
607       if(start)
608       {
609          if(escaped)
610          {
611             escaped = false;
612             output--;
613
614             // ADDED THIS HERE...
615             if(output != string)
616                *output = *string;
617          }
618          else if(escapeBackSlashes && *string == '\\')
619             escaped = true;
620          else if(*string == '\"')
621          {
622             if(quoted)
623             {
624                *output = '\0';
625                quoted = false;
626             }
627             else
628             {
629                memmove(start + 1, start, string - (char *)start);
630                start++;
631             }
632          }
633          else if(*string == ' ' && !quoted)
634          {
635             tokens[count++] = start;
636             *output = '\0';
637             start = null;
638          }
639       }
640       else if(*string != ' ')
641       {
642          if(*string == '\"')
643          {
644             quoted = true;
645             start = output + 1;
646          }
647          else
648          {
649             start = output;
650             if(*string == '\\' && escapeBackSlashes)
651                escaped = true;
652          }
653       }
654    }
655    if(start && count < maxTokens)
656    {
657       tokens[count++] = start;
658       *output = '\0';
659    }
660    return count;
661 }
662
663 public int TokenizeWith(char * string, int maxTokens, char* tokens[], char * tokenizers, bool escapeBackSlashes)
664 {
665    int count = 0;
666    bool quoted = false;
667    byte * start = null;
668    bool escaped = false;
669    char * output = string;
670    bool quotedFromStart = false;
671
672    for(; *string && count < maxTokens; string++, output++)
673    {
674       if(output != string)
675          *output = *string;
676       if(start)
677       {
678          if(escaped)
679          {
680             escaped = false;
681             output--;
682             if(output != string)
683                *output = *string;
684          }
685          else if(escapeBackSlashes && *string == '\\')
686             escaped = true;
687          else if(*string == '\"')
688          {
689             if(quoted)
690             {
691                if(quotedFromStart)
692                   *output = '\0';
693                quotedFromStart = false;
694                quoted = false;
695             }
696             else
697                quoted = true;
698          }
699          else if(strchr(tokenizers, *string) && !quoted)
700          {
701             tokens[count++] = start;
702             *output = '\0';
703             start = null;
704          }
705          // MOVED THIS INSIDE IF ABOVE...
706          //if(output != string)
707          //   *output = *string;
708       }
709       else if(!strchr(tokenizers, *string))
710       {
711          if(*string == '\"')
712          {
713             quotedFromStart = true;
714             quoted = true;
715             start = output + 1;
716          }
717          else
718          {
719             start = output;
720             if(*string == '\\' && escapeBackSlashes)
721                escaped = true;
722          }
723       }
724    }
725    if(start && count < maxTokens)
726    {
727       tokens[count++] = start;
728       *output = '\0';
729    }
730    return count;
731 }
732
733 public char * TrimLSpaces(char * string, char * output)
734 {
735    int c;
736    for(c = 0; string[c] && string[c] == ' '; c++);
737    strcpy(output, string + c);
738    return output;
739 }
740
741 public char * TrimRSpaces(char * string, char * output)
742 {
743    int c;
744    for(c = strlen(string)-1; c >= 0 && string[c] == ' '; c--);
745    if(c >= 0)
746    {
747       memmove(output, string, c+1);
748       output[c+1] = '\0';
749    }
750    else
751       output[0] = '\0';
752    return output;
753 }
754
755 public void ChangeCh(char * string, char ch1, char ch2)
756 {
757    int c;
758    for(c=0; string[c]; c++)
759       if(string[c] == ch1) string[c] = ch2;
760 }
761
762 public void RepeatCh(char * string, int count, char ch)
763 {
764    int c;
765    for(c=0; c<count; c++)
766       string[c] = ch;
767    string[c] = 0;
768 }
769
770 public char * CopyString(char * string)
771 {
772    if(string)
773    {
774       int len = strlen(string);
775       char * destination = new char[len+1];
776       if(destination)
777          memcpy(destination, string, len + 1);
778       return destination;
779    }
780    else
781       return null;
782 }
783
784 public bool GetString(char ** buffer, char * string, int max)
785 {
786    int c;
787    char ch;
788    bool quoted = false;
789    bool result = true;
790    if(!**buffer) { string[0]=0; return false; }
791
792    for(;;)
793    {
794       if(!(ch = *((*buffer)++)))
795          result = false;
796       if( (ch!='\n') && (ch!='\r') && (ch!=' ') && (ch!=',') && (ch!='\t'))
797          break;
798       if(!*(*buffer)) break;
799    }
800    if(result)
801    {
802       for(c=0; c<max-1; c++)
803       {
804          if(!quoted && ((ch=='\n')||(ch=='\r')||(ch==' ')||(ch==',')||(ch=='\t')))
805          {
806             result = true;
807             break;
808          }
809          if(ch == '\"')
810          {
811             quoted ^= 1;
812             c--;
813          }
814          else
815             string[c]=ch;
816
817          if(!(ch = *(*buffer)))
818          {
819             c++;
820             //result = false;
821             break;            
822          }
823          (*buffer)++;
824       }
825       string[c]=0;
826    }
827    return result;
828 }
829
830 public int GetValue(char ** buffer)
831 {
832    char string[20];
833    GetString(buffer,string,20);
834    return atoi(string);
835 }
836
837 public uint GetHexValue(char ** buffer)
838 {
839    char string[20];
840    GetString(buffer,string,20);
841    return strtoul(string, null, 16);
842 }
843
844 public char * StripQuotes(char * string, char * output)
845 {
846    int len;
847
848    strcpy(output,(string[0] == '\"') ? (string+1) : string);
849    len = strlen(output);
850    if(len && output[len-1] == '\"')
851       output[len-1] = '\0';
852    return output;
853 }
854
855 public double FloatFromString(char * string)
856 {
857    int c, dig;
858    float dec = 0,res = 0;
859    int neg = 1;
860    char ch;
861    for(c = 0; string[c]; c++)
862    {
863       ch = string[c];
864       if(ch == ' ') continue;
865       if(ch == '-')
866       {
867          if(neg == -1) break;
868          neg = -1;
869       }
870       else if((ch == '.') && !dec)
871          dec = 10;
872       else if(isdigit(ch))
873       {
874          dig = ch - '0';
875          if(dec)
876          {
877             res += dig / dec;
878             dec *= 10;
879          }
880          else
881             res = res * 10 + dig;
882       }
883       else
884          break;
885    }
886    return neg * res;
887 }
888
889 public bool GetAlNum(char ** buffer, char * string, int max)
890 {
891    int c;
892    char ch;
893    bool result = true;
894    if(!**buffer) { string[0]=0; return false; }
895
896    for(;;)
897    {
898       if(!(ch = *((*buffer)++)))
899          result = false;
900       if(isalnum(ch))
901          break;
902       if(!*(*buffer)) break;
903    }
904    if(result)
905    {
906       for(c=0; c<max-1; c++)
907       {
908          if(!isalnum(ch))
909          {
910             result = true;
911             break;
912          }
913          string[c]=ch;
914          if(!(ch = *(*buffer)))
915          {
916             c++;
917             break;            
918          }
919          (*buffer)++;
920       }
921       string[c]=0;
922    }
923    return result;
924 }