0768c5086410cfe35b4caf93f71c319210519316
[alexxy/gromacs.git] / src / gromacs / utility / cstringutil.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 /* This file is completely threadsafe - keep it that way! */
38 #include "gmxpre.h"
39
40 #include "cstringutil.h"
41
42 #include "config.h"
43
44 #include <assert.h>
45 #include <ctype.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <time.h>
50
51 #include <sys/types.h>
52 #ifdef HAVE_SYS_TIME_H
53 #include <sys/time.h>
54 #endif
55 #ifdef HAVE_PWD_H
56 #include <pwd.h>
57 #endif
58 #ifdef HAVE_UNISTD_H
59 #include <unistd.h>
60 #endif
61
62 #include "gromacs/utility/basedefinitions.h"
63 #include "gromacs/utility/basenetwork.h"
64 #include "gromacs/utility/fatalerror.h"
65 #include "gromacs/utility/futil.h"
66 #include "gromacs/utility/smalloc.h"
67
68 int continuing(char *s)
69 {
70     int sl;
71     assert(s);
72
73     rtrim(s);
74     sl = strlen(s);
75     if ((sl > 0) && (s[sl-1] == CONTINUE))
76     {
77         s[sl-1] = 0;
78         return TRUE;
79     }
80     else
81     {
82         return FALSE;
83     }
84 }
85
86
87
88 char *fgets2(char *line, int n, FILE *stream)
89 {
90     char *c;
91     if (fgets(line, n, stream) == NULL)
92     {
93         return NULL;
94     }
95     if ((c = strchr(line, '\n')) != NULL)
96     {
97         *c = '\0';
98     }
99     else
100     {
101         /* A line not ending in a newline can only occur at the end of a file,
102          * or because of n being too small.
103          * Since both cases occur very infrequently, we can check for EOF.
104          */
105         if (!feof(stream))
106         {
107             gmx_fatal(FARGS, "An input file contains a line longer than %d characters, while the buffer passed to fgets2 has size %d. The line starts with: '%20.20s'", n, n, line);
108         }
109     }
110     if ((c = strchr(line, '\r')) != NULL)
111     {
112         *c = '\0';
113     }
114
115     return line;
116 }
117
118 void strip_comment (char *line)
119 {
120     char *c;
121
122     if (!line)
123     {
124         return;
125     }
126
127     /* search for a comment mark and replace it by a zero */
128     if ((c = strchr(line, COMMENTSIGN)) != NULL)
129     {
130         (*c) = 0;
131     }
132 }
133
134 void upstring (char *str)
135 {
136     int i;
137
138     for (i = 0; (i < (int)strlen(str)); i++)
139     {
140         str[i] = toupper(str[i]);
141     }
142 }
143
144 void ltrim (char *str)
145 {
146     char *tr;
147     int   i, c;
148
149     if (NULL == str)
150     {
151         return;
152     }
153
154     c = 0;
155     while (('\0' != str[c]) && isspace(str[c]))
156     {
157         c++;
158     }
159     if (c > 0)
160     {
161         for (i = c; ('\0' != str[i]); i++)
162         {
163             str[i-c] = str[i];
164         }
165         str[i-c] = '\0';
166     }
167 }
168
169 void rtrim (char *str)
170 {
171     int nul;
172
173     if (NULL == str)
174     {
175         return;
176     }
177
178     nul = strlen(str)-1;
179     while ((nul > 0) && ((str[nul] == ' ') || (str[nul] == '\t')) )
180     {
181         str[nul] = '\0';
182         nul--;
183     }
184 }
185
186 void trim (char *str)
187 {
188     ltrim (str);
189     rtrim (str);
190 }
191
192 char *
193 gmx_ctime_r(const time_t *clock, char *buf, int n)
194 {
195 #ifdef _MSC_VER
196     /* Windows */
197     ctime_s( buf, n, clock );
198 #elif defined(GMX_NATIVE_WINDOWS)
199     char *tmpbuf = ctime( clock );
200     strncpy(buf, tmpbuf, n-1);
201     buf[n-1] = '\0';
202 #elif (defined(__sun))
203     /*Solaris*/
204     ctime_r(clock, buf, n);
205 #else
206     char tmpbuf[STRLEN];
207     ctime_r(clock, tmpbuf);
208     strncpy(buf, tmpbuf, n-1);
209     buf[n-1] = '\0';
210 #endif
211     return buf;
212 }
213
214 void nice_header (FILE *out, const char *fn)
215 {
216     const char    *unk = "onbekend";
217     time_t         clock;
218     const char    *user;
219     int            gh;
220 #ifdef HAVE_PWD_H
221     uid_t          uid;
222 #else
223     int            uid;
224 #endif
225     char           buf[256] = "";
226     char           timebuf[STRLEN];
227 #ifdef HAVE_PWD_H
228     struct passwd *pw;
229 #endif
230
231     /* Print a nice header above the file */
232     time(&clock);
233     fprintf (out, "%c\n", COMMENTSIGN);
234     fprintf (out, "%c\tFile '%s' was generated\n", COMMENTSIGN, fn ? fn : unk);
235
236 #ifdef HAVE_PWD_H
237     uid  = getuid();
238     pw   = getpwuid(uid);
239     gh   = gmx_gethostname(buf, 255);
240     /* pw returns null on error (e.g. compute nodes lack /etc/passwd) */
241     user = pw ? pw->pw_name : unk;
242 #else
243     uid  = 0;
244     gh   = -1;
245     user = unk;
246 #endif
247
248     gmx_ctime_r(&clock, timebuf, STRLEN);
249     fprintf (out, "%c\tBy user: %s (%d)\n", COMMENTSIGN,
250              user ? user : unk, (int) uid);
251     fprintf(out, "%c\tOn host: %s\n", COMMENTSIGN, (gh == 0) ? buf : unk);
252
253     fprintf (out, "%c\tAt date: %s", COMMENTSIGN, timebuf);
254     fprintf (out, "%c\n", COMMENTSIGN);
255 }
256
257
258 int gmx_strcasecmp_min(const char *str1, const char *str2)
259 {
260     char ch1, ch2;
261
262     do
263     {
264         do
265         {
266             ch1 = toupper(*(str1++));
267         }
268         while ((ch1 == '-') || (ch1 == '_'));
269         do
270         {
271             ch2 = toupper(*(str2++));
272         }
273         while ((ch2 == '-') || (ch2 == '_'));
274
275         if (ch1 != ch2)
276         {
277             return (ch1-ch2);
278         }
279     }
280     while (ch1);
281     return 0;
282 }
283
284 int gmx_strncasecmp_min(const char *str1, const char *str2, int n)
285 {
286     char  ch1, ch2;
287     char *stri1, *stri2;
288
289     stri1 = (char *)str1;
290     stri2 = (char *)str2;
291     do
292     {
293         do
294         {
295             ch1 = toupper(*(str1++));
296         }
297         while ((ch1 == '-') || (ch1 == '_'));
298         do
299         {
300             ch2 = toupper(*(str2++));
301         }
302         while ((ch2 == '-') || (ch2 == '_'));
303
304         if (ch1 != ch2)
305         {
306             return (ch1-ch2);
307         }
308     }
309     while (ch1 && (str1-stri1 < n) && (str2-stri2 < n));
310     return 0;
311 }
312
313 int gmx_strcasecmp(const char *str1, const char *str2)
314 {
315     char ch1, ch2;
316
317     do
318     {
319         ch1 = toupper(*(str1++));
320         ch2 = toupper(*(str2++));
321         if (ch1 != ch2)
322         {
323             return (ch1-ch2);
324         }
325     }
326     while (ch1);
327     return 0;
328 }
329
330 int gmx_strncasecmp(const char *str1, const char *str2, int n)
331 {
332     char ch1, ch2;
333
334     if (n == 0)
335     {
336         return 0;
337     }
338
339     do
340     {
341         ch1 = toupper(*(str1++));
342         ch2 = toupper(*(str2++));
343         if (ch1 != ch2)
344         {
345             return (ch1-ch2);
346         }
347         n--;
348     }
349     while (ch1 && n);
350     return 0;
351 }
352
353 char *gmx_strdup(const char *src)
354 {
355     char *dest;
356
357     snew(dest, strlen(src)+1);
358     strcpy(dest, src);
359
360     return dest;
361 }
362
363 char *
364 gmx_strndup(const char *src, int n)
365 {
366     int   len;
367     char *dest;
368
369     len = strlen(src);
370     if (len > n)
371     {
372         len = n;
373     }
374     snew(dest, len+1);
375     strncpy(dest, src, len);
376     dest[len] = 0;
377     return dest;
378 }
379
380 /* Magic hash init number for Dan J. Bernsteins algorithm.
381  * Do NOT use any other value unless you really know what you are doing.
382  */
383 const unsigned int
384     gmx_string_hash_init = 5381;
385
386
387 unsigned int
388 gmx_string_fullhash_func(const char *s, unsigned int hash_init)
389 {
390     int c;
391
392     while ((c = (*s++)) != '\0')
393     {
394         hash_init = ((hash_init << 5) + hash_init) ^ c; /* (hash * 33) xor c */
395     }
396     return hash_init;
397 }
398
399 unsigned int
400 gmx_string_hash_func(const char *s, unsigned int hash_init)
401 {
402     int c;
403
404     while ((c = toupper(*s++)) != '\0')
405     {
406         if (isalnum(c))
407         {
408             hash_init = ((hash_init << 5) + hash_init) ^ c;            /* (hash * 33) xor c */
409         }
410     }
411     return hash_init;
412 }
413
414 int
415 gmx_wcmatch(const char *pattern, const char *str)
416 {
417     while (*pattern)
418     {
419         if (*pattern == '*')
420         {
421             /* Skip multiple wildcards in a sequence */
422             while (*pattern == '*' || *pattern == '?')
423             {
424                 ++pattern;
425                 /* For ?, we need to check that there are characters left
426                  * in str. */
427                 if (*pattern == '?')
428                 {
429                     if (*str == 0)
430                     {
431                         return GMX_NO_WCMATCH;
432                     }
433                     else
434                     {
435                         ++str;
436                     }
437                 }
438             }
439             /* If the pattern ends after the star, we have a match */
440             if (*pattern == 0)
441             {
442                 return 0;
443             }
444             /* Match the rest against each possible suffix of str */
445             while (*str)
446             {
447                 /* Only do the recursive call if the first character
448                  * matches. We don't have to worry about wildcards here,
449                  * since we have processed them above. */
450                 if (*pattern == *str)
451                 {
452                     int rc;
453                     /* Match the suffix, and return if a match or an error */
454                     rc = gmx_wcmatch(pattern, str);
455                     if (rc != GMX_NO_WCMATCH)
456                     {
457                         return rc;
458                     }
459                 }
460                 ++str;
461             }
462             /* If no suffix of str matches, we don't have a match */
463             return GMX_NO_WCMATCH;
464         }
465         else if ((*pattern == '?' && *str != 0) || *pattern == *str)
466         {
467             ++str;
468         }
469         else
470         {
471             return GMX_NO_WCMATCH;
472         }
473         ++pattern;
474     }
475     /* When the pattern runs out, we have a match if the string has ended. */
476     return (*str == 0) ? 0 : GMX_NO_WCMATCH;
477 }
478
479 char *wrap_lines(const char *buf, int line_width, int indent, gmx_bool bIndentFirst)
480 {
481     char    *b2;
482     int      i, i0, i2, j, b2len, lspace = 0, l2space = 0;
483     gmx_bool bFirst, bFitsOnLine;
484
485     /* characters are copied from buf to b2 with possible spaces changed
486      * into newlines and extra space added for indentation.
487      * i indexes buf (source buffer) and i2 indexes b2 (destination buffer)
488      * i0 points to the beginning of the current line (in buf, source)
489      * lspace and l2space point to the last space on the current line
490      * bFirst is set to prevent indentation of first line
491      * bFitsOnLine says if the first space occurred before line_width, if
492      * that is not the case, we have a word longer than line_width which
493      * will also not fit on the next line, so we might as well keep it on
494      * the current line (where it also won't fit, but looks better)
495      */
496
497     b2    = NULL;
498     b2len = strlen(buf)+1+indent;
499     snew(b2, b2len);
500     i0 = i2 = 0;
501     if (bIndentFirst)
502     {
503         for (i2 = 0; (i2 < indent); i2++)
504         {
505             b2[i2] = ' ';
506         }
507     }
508     bFirst = TRUE;
509     do
510     {
511         l2space = -1;
512         /* find the last space before end of line */
513         for (i = i0; ((i-i0 < line_width) || (l2space == -1)) && (buf[i]); i++)
514         {
515             b2[i2++] = buf[i];
516             /* remember the position of a space */
517             if (buf[i] == ' ')
518             {
519                 lspace  = i;
520                 l2space = i2-1;
521             }
522             /* if we have a newline before the line is full, reset counters */
523             if (buf[i] == '\n' && buf[i+1])
524             {
525                 i0     = i+1;
526                 b2len += indent;
527                 srenew(b2, b2len);
528                 /* add indentation after the newline */
529                 for (j = 0; (j < indent); j++)
530                 {
531                     b2[i2++] = ' ';
532                 }
533             }
534         }
535         /* If we are at the last newline, copy it */
536         if (buf[i] == '\n' && !buf[i+1])
537         {
538             b2[i2++] = buf[i++];
539         }
540         /* if we're not at the end of the string */
541         if (buf[i])
542         {
543             /* check if one word does not fit on the line */
544             bFitsOnLine = (i-i0 <= line_width);
545             /* reset line counters to just after the space */
546             i0 = lspace+1;
547             i2 = l2space+1;
548             /* if the words fit on the line, and we're beyond the indentation part */
549             if ( (bFitsOnLine) && (l2space >= indent) )
550             {
551                 /* start a new line */
552                 b2[l2space] = '\n';
553                 /* and add indentation */
554                 if (indent)
555                 {
556                     if (bFirst)
557                     {
558                         line_width -= indent;
559                         bFirst      = FALSE;
560                     }
561                     b2len += indent;
562                     srenew(b2, b2len);
563                     for (j = 0; (j < indent); j++)
564                     {
565                         b2[i2++] = ' ';
566                     }
567                     /* no extra spaces after indent; */
568                     while (buf[i0] == ' ')
569                     {
570                         i0++;
571                     }
572                 }
573             }
574         }
575     }
576     while (buf[i]);
577     b2[i2] = '\0';
578
579     return b2;
580 }
581
582 gmx_int64_t
583 str_to_int64_t(const char *str, char **endptr)
584 {
585 #ifndef _MSC_VER
586     return strtoll(str, endptr, 10);
587 #else
588     return _strtoi64(str, endptr, 10);
589 #endif
590 }
591
592 char *gmx_step_str(gmx_int64_t i, char *buf)
593 {
594     sprintf(buf, "%"GMX_PRId64, i);
595     return buf;
596 }
597
598 void parse_digits_from_plain_string(const char *digitstring, int *ndigits, int **digitlist)
599 {
600     int i;
601
602     if (NULL == digitstring)
603     {
604         *ndigits   = 0;
605         *digitlist = NULL;
606         return;
607     }
608
609     *ndigits = strlen(digitstring);
610
611     snew(*digitlist, *ndigits);
612
613     for (i = 0; i < *ndigits; i++)
614     {
615         if (digitstring[i] < '0' || digitstring[i] > '9')
616         {
617             gmx_fatal(FARGS, "Invalid character in digit-only string: '%c'\n",
618                       digitstring[i]);
619         }
620         (*digitlist)[i] = digitstring[i] - '0';
621     }
622 }
623
624 static void parse_digits_from_csv_string(const char gmx_unused *digitstring, int gmx_unused *ndigits, int gmx_unused *digitlist)
625 {
626     /* TODO Implement csv format to support (e.g.) more than 10
627        different GPUs in a node. */
628     gmx_incons("Not implemented yet");
629 }