Simple fixes to get navit compiling with MSVC
[profile/ivi/navit.git] / navit / navit / util.c
1 /**
2  * Navit, a modular navigation system.
3  * Copyright (C) 2005-2008 Navit Team
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * version 2 as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA  02110-1301, USA.
18  */
19
20 #include <stdlib.h>
21 #include <glib.h>
22 #include <ctype.h>
23 #include <stdarg.h>
24 #include <time.h>
25 #include <limits.h>
26 #include <string.h>
27
28 #ifdef _POSIX_C_SOURCE
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #endif
33 #ifdef _MSC_VER
34 typedef int ssize_t ;
35 #endif
36 #include "util.h"
37 #include "debug.h"
38 #include "config.h"
39
40 void
41 strtoupper(char *dest, const char *src)
42 {
43         while (*src)
44                 *dest++=toupper(*src++);
45         *dest='\0';
46 }
47
48 void
49 strtolower(char *dest, const char *src)
50 {
51         while (*src)
52                 *dest++=tolower(*src++);
53         *dest='\0';
54 }
55
56
57 static void
58 hash_callback(gpointer key, gpointer value, gpointer user_data)
59 {
60         GList **l=user_data;
61         *l=g_list_prepend(*l, value);
62 }
63
64 GList *
65 g_hash_to_list(GHashTable *h)
66 {
67         GList *ret=NULL;
68         g_hash_table_foreach(h, hash_callback, &ret);
69
70         return ret;
71 }
72
73 static void
74 hash_callback_key(gpointer key, gpointer value, gpointer user_data)
75 {
76         GList **l=user_data;
77         *l=g_list_prepend(*l, key);
78 }
79
80 GList *
81 g_hash_to_list_keys(GHashTable *h)
82 {
83         GList *ret=NULL;
84         g_hash_table_foreach(h, hash_callback_key, &ret);
85
86         return ret;
87 }
88
89 gchar *
90 g_strconcat_printf(gchar *buffer, gchar *fmt, ...)
91 {
92         gchar *str,*ret;
93         va_list ap;
94
95         va_start(ap, fmt);
96         str=g_strdup_vprintf(fmt, ap);
97         va_end(ap);
98         if (! buffer)
99                 return str;
100         ret=g_strconcat(buffer, str, NULL);
101         g_free(buffer);
102         g_free(str);
103         return ret;
104 }
105
106 #ifndef HAVE_GLIB
107 int g_utf8_strlen_force_link(gchar *buffer, int max);
108 int
109 g_utf8_strlen_force_link(gchar *buffer, int max)
110 {
111         return g_utf8_strlen(buffer, max);
112 }
113 #endif
114
115 #if defined(_WIN32) || defined(__CEGCC__)
116 #include <windows.h>
117 #include <sys/types.h>
118 #endif
119
120 #if defined(_WIN32) || defined(__CEGCC__) || defined (__APPLE__) || defined(HAVE_API_ANDROID)
121 #include <stdio.h>
122 char *stristr(const char *String, const char *Pattern)
123 {
124       char *pptr, *sptr, *start;
125
126       for (start = (char *)String; *start != (int)NULL; start++)
127       {
128             /* find start of pattern in string */
129             for ( ; ((*start!=(int)NULL) && (toupper(*start) != toupper(*Pattern))); start++)
130                   ;
131             if ((int)NULL == *start)
132                   return NULL;
133
134             pptr = (char *)Pattern;
135             sptr = (char *)start;
136
137             while (toupper(*sptr) == toupper(*pptr))
138             {
139                   sptr++;
140                   pptr++;
141
142                   /* if end of pattern then pattern was found */
143
144                   if ((int)NULL == *pptr)
145                         return (start);
146             }
147       }
148       return NULL;
149 }
150
151 #ifndef SIZE_MAX
152 # define SIZE_MAX ((size_t) -1)
153 #endif
154 #ifndef SSIZE_MAX
155 # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
156 #endif
157 #if !HAVE_FLOCKFILE
158 # undef flockfile
159 # define flockfile(x) ((void) 0)
160 #endif
161 #if !HAVE_FUNLOCKFILE
162 # undef funlockfile
163 # define funlockfile(x) ((void) 0)
164 #endif
165
166 /* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW.  */
167 #ifndef EOVERFLOW
168 # define EOVERFLOW E2BIG
169 #endif
170
171
172 #ifndef HAVE_GETDELIM
173 /**
174  * Read the part of a file up to a delimiter to a string.
175  * <p> 
176  * Read up to (and including) a DELIMITER from FP into *LINEPTR (and
177    NUL-terminate it).  
178  * @param lineptr Pointer to a pointer returned from malloc (or
179    NULL), pointing to a buffer. It is realloc'ed as
180    necessary and will receive the data read.
181  * @param n Size of the buffer.  
182  *
183  * @return Number of characters read (not including
184    the null terminator), or -1 on error or EOF.
185 */
186 ssize_t
187 getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp)
188 {
189   int result;
190   size_t cur_len = 0;
191
192   if (lineptr == NULL || n == NULL || fp == NULL)
193     {
194       return -1;
195     }
196
197   flockfile (fp);
198
199   if (*lineptr == NULL || *n == 0)
200     {
201       *n = 120;
202       *lineptr = (char *) realloc (*lineptr, *n);
203       if (*lineptr == NULL)
204         {
205           result = -1;
206           goto unlock_return;
207         }
208     }
209
210   for (;;)
211     {
212       int i;
213
214       i = getc (fp);
215       if (i == EOF)
216         {
217           result = -1;
218           break;
219         }
220
221       /* Make enough space for len+1 (for final NUL) bytes.  */
222       if (cur_len + 1 >= *n)
223         {
224           size_t needed_max=SIZE_MAX;
225           size_t needed = 2 * *n + 1;   /* Be generous. */
226           char *new_lineptr;
227           if (needed_max < needed)
228             needed = needed_max;
229           if (cur_len + 1 >= needed)
230             {
231               result = -1;
232               goto unlock_return;
233             }
234
235           new_lineptr = (char *) realloc (*lineptr, needed);
236           if (new_lineptr == NULL)
237             {
238               result = -1;
239               goto unlock_return;
240             }
241
242           *lineptr = new_lineptr;
243           *n = needed;
244         }
245
246       (*lineptr)[cur_len] = i;
247       cur_len++;
248
249       if (i == delimiter)
250         break;
251     }
252   (*lineptr)[cur_len] = '\0';
253   result = cur_len ? cur_len : result;
254
255  unlock_return:
256   funlockfile (fp); /* doesn't set errno */
257
258   return result;
259 }
260 #endif
261
262 #ifndef HAVE_GETLINE
263 ssize_t
264 getline (char **lineptr, size_t *n, FILE *stream)
265 {
266   return getdelim (lineptr, n, '\n', stream);
267 }
268 #endif
269
270 #if defined(_UNICODE)
271 wchar_t* newSysString(const char *toconvert)
272 {
273         int newstrlen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, toconvert, -1, 0, 0);
274         wchar_t *newstring = g_new(wchar_t,newstrlen);
275         MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, toconvert, -1, newstring, newstrlen) ;
276         return newstring;
277 }
278 #else
279 char * newSysString(const char *toconvert)
280 {
281         return g_strdup(toconvert);
282 }
283 #endif
284 #endif
285
286 #if defined(_MSC_VER) || (!defined(HAVE_GETTIMEOFDAY) && defined(HAVE_API_WIN32_BASE))
287 /**
288  * Impements a simple incomplete version of gettimeofday. Only usefull for messuring
289  * time spans, not the real time of day.
290  */
291 int gettimeofday(struct timeval *time, void *local)
292 {
293   int milliseconds = GetTickCount();
294
295   time->tv_sec = milliseconds/1000;
296   time->tv_usec = (milliseconds - (time->tv_sec * 1000)) * 1000;
297
298   return 0;
299 }
300 #endif
301 /**
302  * Convert an ISO 8601-style time string into epoch time.
303  *
304  * @param iso8601 Pointer to a string containing the time in ISO 8601 format.
305  *
306  * @return An unsigned integer representing the number of seconds elapsed since January 1, 1970, 00:00:00 UTC.
307  */
308 unsigned int
309 iso8601_to_secs(char *iso8601)
310 {
311         int a,b,d,val[6],i=0;
312         char *start=iso8601,*pos=iso8601;
313         while (*pos && i < 6) {
314                 if (*pos < '0' || *pos > '9') {
315                         val[i++]=atoi(start);
316                         pos++;
317                         start=pos;
318                 } 
319                 if(*pos)
320                         pos++;
321         }
322         
323         a=val[0]/100;
324         b=2-a+a/4;
325
326         if (val[1] < 2) {
327                 val[0]--;
328                 val[1]+=12;
329         }
330
331         d=1461*(val[0]+4716)/4+306001*(val[1]+1)/10000+val[2]+b-2442112;
332
333         return ((d*24+val[3])*60+val[4])*60+val[5];
334 }
335
336 /**
337  * Output local system time in ISO 8601 format.
338  *
339  * @return Pointer to a string containing the time in ISO 8601 format
340  */
341 char *
342 current_to_iso8601(void)
343 {
344         char *timep=NULL;
345 #ifdef HAVE_API_WIN32_BASE
346         SYSTEMTIME ST;
347         GetSystemTime(&ST);
348         timep=g_strdup_printf("%d-%02d-%02dT%02d:%02d:%02dZ",ST.wYear,ST.wMonth,ST.wDay,ST.wHour,ST.wMinute,ST.wSecond);
349 #else
350         char buffer[32];
351         time_t tnow;
352         struct tm *tm;
353         tnow = time(0);
354         tm = gmtime(&tnow);
355         if (tm) {
356                 strftime(buffer, sizeof(buffer), "%Y-%m-%dT%TZ", tm);
357                 timep=g_strdup(buffer); 
358         }
359 #endif
360         return timep;
361 }
362
363
364 struct spawn_process_info {
365 #ifdef HAVE_API_WIN32_BASE
366         PROCESS_INFORMATION pr;
367 #else
368         pid_t pid; // = -1 if non-blocking spawn isn't supported
369         int status; // exit status if non-blocking spawn isn't supported
370 #endif
371 };
372
373
374 /**
375  * Escape and quote string for shell
376  *
377  * @param in arg string to escape
378  * @returns escaped string
379  */
380 char *
381 shell_escape(char *arg) 
382 {
383         char *r;
384         int arglen=strlen(arg);
385         int i,j,rlen;
386 #ifdef HAVE_API_WIN32_BASE
387         {
388                 int bscount=0;
389                 rlen=arglen+3;
390                 r=g_new(char,rlen);
391                 r[0]='"';
392                 for(i=0,j=1;i<arglen;i++) {
393                         if(arg[i]=='\\') {
394                                 bscount++;
395                                 if(i==(arglen-1)) {
396                                         // Most special case - last char is 
397                                         // backslash. We can't escape it inside
398                                         // quoted string due to Win unescaping 
399                                         // rules so quote should be closed 
400                                         // before backslashes and these
401                                         // backslashes shouldn't be doubled
402                                         rlen+=bscount;
403                                         r=g_realloc(r,rlen);
404                                         r[j++]='"';
405                                         memset(r+j,'\\',bscount);
406                                         j+=bscount;
407                                 }
408                         } else {
409                                 //Any preceeding backslashes will be doubled.
410                                 bscount*=2;
411                                 // Double quote needs to be preceeded by 
412                                 // at least one backslash
413                                 if(arg[i]=='"')
414                                         bscount++;
415                                 if(bscount>0) {
416                                         rlen+=bscount;
417                                         r=g_realloc(r,rlen);
418                                         memset(r+j,'\\',bscount);
419                                         j+=bscount;
420                                         bscount=0;
421                                 }
422                                 r[j++]=arg[i];
423                                 if(i==(arglen-1)) {
424                                         r[j++]='"';
425                                 }
426                         }
427                 }
428                 r[j++]=0;
429         }
430 #else
431         {
432                 // Will use hard quoting for the whole string
433                 // and replace each singular quote found with a '\'' sequence.
434                 rlen=arglen+3;
435                 r=g_new(char,rlen);
436                 r[0]='\'';
437                 for(i=0,j=1;i<arglen;i++) {
438                         if(arg[i]=='\'') {
439                                 rlen+=3;
440                                 r=g_realloc(r,rlen);
441                                 g_strlcpy(r+j,"'\\''",rlen-j);
442                         } else {
443                                 r[j++]=arg[i];
444                         }
445                 }
446                 r[j++]='\'';
447                 r[j++]=0;
448         }
449 #endif
450         return r;
451 }
452
453 #ifndef _POSIX_C_SOURCE
454 static char*
455 spawn_process_compose_cmdline(char **argv)
456 {
457         int i,j;
458         char *cmdline=shell_escape(argv[0]);
459         for(i=1,j=strlen(cmdline);argv[i];i++) {
460                 char *arg=shell_escape(argv[i]);
461                 int arglen=strlen(arg);
462                 cmdline[j]=' ';
463                 cmdline=g_realloc(cmdline,j+1+arglen+1);
464                 memcpy(cmdline+j+1,arg,arglen+1);
465                 g_free(arg);
466                 j=j+1+arglen;
467         }
468         return cmdline;
469 }
470 #endif
471
472 #ifdef _POSIX_C_SOURCE
473
474 #if 0 /* def _POSIX_THREADS */
475 #define spawn_process_sigmask(how,set,old) pthread_sigmask(how,set,old)
476 #else
477 #define spawn_process_sigmask(how,set,old) sigprocmask(how,set,old)
478 #endif
479
480 GList *spawn_process_children=NULL;
481
482 #endif
483
484
485 /**
486  * Call external program
487  *
488  * @param in argv NULL terminated list of parameters,
489  *    zeroeth argument is program name
490  * @returns 0 - success, >0 - return code, -1 - error
491  */
492 struct spawn_process_info*
493 spawn_process(char **argv)
494 {
495         struct spawn_process_info*r=g_new(struct spawn_process_info,1);
496 #ifdef _POSIX_C_SOURCE
497         {
498                 pid_t pid;
499                 
500                 sigset_t set, old;
501                 sigemptyset(&set);
502                 sigaddset(&set,SIGCHLD);
503                 spawn_process_sigmask(SIG_BLOCK,&set,&old);
504                 pid=fork();
505                 if(pid==0) {
506                         execvp(argv[0], argv);
507                         /*Shouldn't reach here*/
508                         exit(1);
509                 } else if(pid>0) {
510                         r->status=-1;
511                         r->pid=pid;
512                         spawn_process_children=g_list_prepend(spawn_process_children,r);
513                 } else {
514                         dbg(0,"fork() returned error.");
515                         g_free(r);
516                         r=NULL;
517                 }
518                 spawn_process_sigmask(SIG_SETMASK,&old,NULL);
519                 return r;
520         }
521 #else
522 #ifdef HAVE_API_WIN32_BASE
523         {
524                 char *cmdline;
525                 DWORD dwRet;
526
527                 // For [desktop] Windows it's adviceable not to use
528                 // first CreateProcess parameter because PATH is not used
529                 // if it is defined.
530                 //
531                 // On WinCE 6.0 I was unable to launch anything
532                 // without first CreateProcess parameter, also it seems that
533                 // no WinCE program has support for quoted strings in arguments.
534                 // So...
535 #ifdef HAVE_API_WIN32_CE
536                 LPWSTR cmd,args;
537                 cmdline=g_strjoinv(" ",argv+1);
538                 args=newSysString(cmdline);
539                 cmd = newSysString(argv[0]);
540                 dwRet=CreateProcess(cmd, args, NULL, NULL, 0, 0, NULL, NULL, NULL, &(r->pr));
541                 dbg(0, "CreateProcess(%s,%s), PID=%i\n",argv[0],cmdline,r->pr.dwProcessId);
542                 g_free(cmd);
543 #else
544                 TCHAR* args;
545                 STARTUPINFO startupInfo;
546                 memset(&startupInfo, 0, sizeof(startupInfo));
547                 startupInfo.cb = sizeof(startupInfo);
548                 cmdline=spawn_process_compose_cmdline(argv);
549                 args=newSysString(cmdline);
550                 dwRet=CreateProcess(NULL, args, NULL, NULL, 0, 0, NULL, NULL, &startupInfo, &(r->pr));
551                 dbg(0, "CreateProcess(%s), PID=%i\n",cmdline,r->pr.dwProcessId);
552 #endif
553                 g_free(cmdline);
554                 g_free(args);
555                 return r;
556         }
557 #else
558         {
559                 char *cmdline=spawn_process_compose_cmdline(argv);
560                 int status;
561                 dbg(0,"Unblocked spawn_process isn't availiable on this platform.\n");
562                 status=system(cmdline);
563                 g_free(cmdline);
564                 r->status=status;
565                 r->pid=0;
566                 return r;
567         }
568 #endif
569 #endif
570 }
571
572 /**
573  * Check external program status
574  *
575  * @param in *pi pointer to spawn_process_info structure
576  * @param in block =0 do not block =1 block until child terminated
577  * @returns -1 - still running, >=0 program exited, 
578  *     =255 trminated abnormally or wasn't run at all.
579  * 
580  */
581 int spawn_process_check_status(struct spawn_process_info *pi, int block)
582 {
583         if(pi==NULL) {
584                 dbg(0,"Trying to get process status of NULL, assuming process is terminated.\n");
585                 return 255;
586         }
587 #ifdef HAVE_API_WIN32_BASE
588         {int failcount=0;
589                 while(1){
590                         DWORD dw;
591                         if(GetExitCodeProcess(pi->pr.hProcess,&dw)) {
592                                 if(dw!=STILL_ACTIVE) {
593                                         return dw;
594                                         break;
595                                 }
596                         } else {
597                                 dbg(0,"GetExitCodeProcess failed. Assuming the process is terminated.");
598                                 return 255;
599                         }
600                         if(!block)
601                                 return -1;
602                 
603                         dw=WaitForSingleObject(pi->pr.hProcess,INFINITE);
604                         if(dw==WAIT_FAILED && failcount++==1) {
605                                 dbg(0,"WaitForSingleObject failed twice. Assuming the process is terminated.");
606                                 return 0;
607                                 break;
608                         }
609                 }
610         }
611 #else
612 #ifdef _POSIX_C_SOURCE
613         if(pi->status!=-1) {
614                 return pi->status;
615         }
616         while(1) {
617                 int status;
618                 pid_t w=waitpid(pi->pid,&status,block?0:WNOHANG);
619                 if(w>0) {
620                         if(WIFEXITED(status))
621                                 pi->status=WEXITSTATUS(status);
622                                 return pi->status;
623                         if(WIFSTOPPED(status)) {
624                                 dbg(0,"child is stopped by %i signal\n",WSTOPSIG(status));
625                         } else if (WIFSIGNALED(status)) {
626                                 dbg(0,"child terminated by signal %i\n",WEXITSTATUS(status));
627                                 pi->status=255;
628                                 return 255;
629                         }
630                         if(!block)
631                                 return -1;
632                 } else if(w==0) {
633                         if(!block)
634                                 return -1;
635                 } else {
636                         if(pi->status!=-1) // Signal handler has changed pi->status while in this function
637                                 return pi->status;
638                         dbg(0,"waitpid() indicated error, reporting process termination.\n");
639                         return 255;
640                 }
641         }
642 #else
643         dbg(0, "Non-blocking spawn_process isn't availiable for this platform, repoting process exit status.\n");
644         return pi->status;
645 #endif
646 #endif
647 }
648
649 void spawn_process_info_free(struct spawn_process_info *pi)
650 {
651         if(pi==NULL)
652                 return;
653 #ifdef HAVE_API_WIN32_BASE
654         CloseHandle(pi->pr.hProcess);
655         CloseHandle(pi->pr.hThread);
656 #endif
657 #ifdef _POSIX_C_SOURCE
658         {
659                 sigset_t set, old;
660                 sigemptyset(&set);
661                 sigaddset(&set,SIGCHLD);
662                 spawn_process_sigmask(SIG_BLOCK,&set,&old);
663                 spawn_process_children=g_list_remove(spawn_process_children,pi);
664                 spawn_process_sigmask(SIG_SETMASK,&old,NULL);
665         }
666 #endif
667         g_free(pi);
668 }
669
670 #ifdef _POSIX_C_SOURCE
671 static void spawn_process_sigchld(int sig)
672 {
673         int status;
674         pid_t pid;
675         while ((pid=waitpid(-1, &status, WNOHANG)) > 0) {
676                 GList *el=g_list_first(spawn_process_children);
677                 while(el) {
678                         struct spawn_process_info *p=el->data;
679                         if(p->pid==pid) {
680                                 p->status=status;
681                         }
682                         el=g_list_next(el);
683                 }
684         }
685 }
686 #endif
687
688 void spawn_process_init()
689 {
690 #ifdef _POSIX_C_SOURCE
691         struct sigaction act;
692         act.sa_handler=spawn_process_sigchld;
693         act.sa_flags=0;
694         sigemptyset(&act.sa_mask);
695         sigaction(SIGCHLD, &act, NULL);
696 #endif
697         return;
698 }
699
700