1 /* $Id$
2  *  Provide functions to compiler-indepenent I/O and file/dirname operations.
3  *  (c) Husky Developers Team
4  *
5  *  Latest version may be foind on http://husky.sourceforge.net
6  *
7  *
8  * HUSKYLIB: common defines, types and functions for HUSKY
9  *
10  * This is part of The HUSKY Fidonet Software project:
11  * see http://husky.sourceforge.net for details
12  *
13  *
14  * HUSKYLIB is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2 of the License, or (at your option) any later version.
18  *
19  * HUSKYLIB is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; see file COPYING. If not, write to the
26  * Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27  *
28  * See also http://www.gnu.org, license may be found here.
29  */
30 
31 /* standard headers */
32 #include <stdio.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <sys/types.h>
36 #include <ctype.h>
37 
38 /* huskylib: compiler.h */
39 #include <compiler.h>
40 
41 
42 /* compiler-dependent headers */
43 #ifdef HAS_PWD_H
44 #include <pwd.h>
45 #endif
46 
47 #ifdef HAS_SYS_UTIME_H
48 #  include <sys/utime.h>
49 #elif defined(HAS_UTIME_H)
50 #  include <utime.h>
51 #endif
52 
53 
54 /* huskylib headers */
55 #define DLLEXPORT
56 #include <huskyext.h>
57 
58 #include <huskylib.h>
59 
60 
61 /***  Declarations & defines  ***********************************************/
62 
63 
64 /***  Implementation  *******************************************************/
65 
66 
67 #ifdef __TURBOC__
68 #pragma warn -sig
69 #endif
70 
getUINT16(FILE * in)71 word getUINT16(FILE *in)
72 {
73    unsigned char dummy;
74 
75    dummy = (unsigned char) getc(in);
76    return (dummy + (unsigned char) getc(in) * 256);
77 }
78 
fputUINT16(FILE * out,word data)79 int fputUINT16(FILE *out, word data)
80 {
81   unsigned char dummy;
82 
83   dummy = (unsigned char)(data % 256);        /*  write high Byte */
84   fputc(dummy, out);
85   dummy = (unsigned char)(data / 256);        /*  write low Byte */
86   return fputc(dummy, out);
87 }
88 
89 #ifdef __TURBOC__
90 #pragma warn +sig
91 #endif
92 
93 
fgetsUntil0(unsigned char * str,size_t n,FILE * f,char * filter)94 signed int fgetsUntil0(unsigned char *str, size_t n, FILE *f, char *filter)
95 {
96    size_t i;
97 
98    for (i=0;i<n-1 ;i++ ) {
99 
100 	  do {
101 		  str[i] = (unsigned char)getc(f);
102 	  } while (filter && *filter && str[i] && strchr(filter, str[i]) != NULL);
103 
104       /*  if end of file */
105       if (feof(f)) {
106          str[i] = 0;
107          return i+1;
108       } /* endif */
109 
110       if (0 == str[i]) {
111          return i+1;
112       } /* endif */
113 
114    } /* endfor */
115 
116    str[n-1] = 0;
117    return n;
118 }
119 
shell_expand(char * str)120 char *shell_expand(char *str)
121 {
122     char *slash = NULL, *ret = NULL, c;
123 #ifdef __UNIX__
124     struct passwd *pw = NULL;
125 #endif
126     char *pfix = NULL;
127 
128     if (str == NULL)
129     {
130         return str;
131     }
132     if (*str == '\0' || str[0] != '~')
133     {
134         return str;
135     }
136     for (slash = str; *slash != '/' && *slash != '\0'
137 #ifndef __UNIX__
138                      && *slash != '\\'
139 #endif
140          ; slash++);
141     c = *slash;
142     *slash = 0;
143 
144     if (str[1] == '\0')
145     {
146         pfix = getenv("HOME");
147 #ifdef __UNIX__
148         if (pfix == NULL)
149         {
150             pw = getpwuid(getuid());
151             if (pw != NULL)
152             {
153                 pfix = pw->pw_dir;
154             }
155         }
156 #endif
157     }
158 #ifdef __UNIX__
159     else
160     {
161         pw = getpwnam(str + 1);
162         if (pw != NULL)
163         {
164             pfix = pw->pw_dir;
165         }
166     }
167 #endif
168     *slash = c;
169 
170     if (pfix == NULL)  /* could not find an expansion */
171     {
172         return str;
173     }
174 
175     ret = smalloc(strlen(slash) + strlen(pfix) + 1);
176     strcpy(ret, pfix);
177     strcat(ret, slash);
178     nfree(str);
179     return ret;
180 }
181 
182 #if defined(__DOS__) && !defined(__FLAT__) || defined(__WIN16__)
183 /* _WINDOWS : 16-bit windows */
184 #define MOVE_FILE_BUFFER_SIZE 16384
185 #else
186 #define MOVE_FILE_BUFFER_SIZE 128000
187 #endif
188 
189 #if defined(__NT__)
190 /* This function helps in dealing with files opened elsewhere with a flag FILE_SHARE_DELETE.
191  * remove() on them will result in state when the file is going to be deleted, it but won't be
192  * actually out of the way until whoever has it opened closes it.
193  * Here is alternative --
194  * rename file first (which will have effect immediately), remove afterwards. */
fix_pending_delete(const char * to)195 void fix_pending_delete(const char *to)
196 {
197 	int size;
198 	char *oldfile;
199 	size = strlen(to);
200 	oldfile = (char*) smalloc(size + 1 +1 + 6);
201 	memcpy(oldfile, to, size);
202 	memcpy(oldfile + size, ".XXXXXX", 8);
203 	if(mktemp(oldfile) == oldfile && !rename(to, oldfile))
204 		remove(oldfile); /* worked just fine, now try remove it */
205 	else /* something went wrong, let's just remove it */
206 		remove(to);
207 	nfree(oldfile);
208 }
209 #endif
210 
move_file(const char * from,const char * to,const int force_rewrite)211 int move_file(const char *from, const char *to, const int force_rewrite)
212 {
213 #if !(defined(USE_SYSTEM_COPY) && (defined(__NT__) || defined(__OS2__)))
214     int rc;
215 
216 #ifdef HAVE_CMPFNAMES  /* check cmpfnames for all OS and remove this condition */
217     if ( cmpfnames((char*)from,(char*)to) == 0 )
218         return 0;
219 #endif
220 
221     w_dbglog( LL_DEBUGZ, __FILE__ ":%u:move_file(%s,%s,%d)", __LINE__, from, to, force_rewrite );
222     if(force_rewrite)
223     {
224       if(fexist(to))
225       {
226 #if defined(__NT__)
227         fix_pending_delete(to);
228 #else
229         remove(to);
230 #endif
231       }
232     }
233     else if(fexist(to)){
234       errno=EEXIST;
235       return -1;
236     }
237 
238     w_dbglog( LL_DEBUGZ, __FILE__ ":%u:move_file()", __LINE__ );
239     rc = rename(from, to);
240     if (!rc) {               /* rename succeeded. fine! */
241 #elif defined(__NT__) && defined(USE_SYSTEM_COPY)
242     int rc;
243 
244 #ifdef HAVE_CMPFNAMES  /* check cmpfnames for all OS and remove this condition */
245     if ( cmpfnames((char*)from,(char*)to) == 0 )
246         return 0;
247 #endif
248 
249     if(force_rewrite)
250     {
251       if(fexist(to))
252       {
253         fix_pending_delete(to);
254       }
255     }
256     else if(fexist(to)){
257       errno=EEXIST;
258       return -1;
259     }
260     rc = MoveFile(from, to);
261     if (rc == TRUE) {
262 #elif defined(__OS2__) && defined(USE_SYSTEM_COPY)
263     USHORT rc;
264 
265 #ifdef HAVE_CMPFNAMES  /* check cmpfnames for all OS and remove this condition */
266     if ( cmpfnames((char*)from,(char*)to) == 0 )
267         return 0;
268 #endif
269 
270     if(force_rewrite)
271       remove(to);
272     else if(fexist(to)){
273       errno=EEXIST;
274       return -1;
275     }
276     rc = DosMove((PSZ)from, (PSZ)to);
277     if (!rc) {
278 #endif
279       return 0;
280     }
281 
282     /* Rename did not succeed, probably because the move is accross
283        file system boundaries. We have to copy the file. */
284 
285     if (copy_file(from, to, force_rewrite))
286     {
287         w_log( LL_WARN, "Moving file from '%s' to '%s' failed, copy over failed too. This may result in loss of information and inconsistent state of the system.", from, to );
288 		return -1;
289     }
290     remove(from);
291     return 0;
292 }
293 
294 
295 int copy_file(const char *from, const char *to, const int force_rewrite)
296 {
297 #if !(defined(USE_SYSTEM_COPY) && (defined(__NT__) || defined(OS2)))
298     char *buffer;
299     size_t read;
300     FILE *fin, *fout;
301     struct stat st;
302 # if defined(_MAKE_DLL_MVC_)
303     struct _utimbuf ut;
304 # else
305     struct utimbuf ut;
306 # endif
307     int fh=-1;
308 
309     w_dbglog( LL_DEBUGZ, __FILE__ ":%u:copy_file(%s,%s,%d)", __LINE__, from, to, force_rewrite );
310 
311 #ifdef HAVE_CMPFNAMES  /* check cmpfnames for all OS and remove this condition */
312     if ( cmpfnames((char*)from,(char*)to) == 0 )
313         return 0;
314 #endif
315 
316     buffer = malloc(MOVE_FILE_BUFFER_SIZE);
317     if (buffer == NULL)	return -1;
318 
319     memset(&st, 0, sizeof(st));
320     if (stat(from, &st))
321     {
322       nfree(buffer);
323       return -1; /* file does not exist */
324     }
325 
326     w_dbglog( LL_DEBUGZ, __FILE__ ":%u:copy_file()", __LINE__);
327     fin = fopen(from, "rb");        /* todo: use open( ..., O_CREAT| ..., ...)
328                                      * to prevent file overwrite */
329     if (fin == NULL) { nfree(buffer); return -1; }
330     w_dbglog( LL_DEBUGZ, __FILE__ ":%u:copy_file()", __LINE__);
331     fh = open( to, (force_rewrite ? 0 : O_EXCL) | O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, S_IREAD | S_IWRITE );
332     if( fh<0 ){
333       fh=errno;
334       fclose(fin);
335       nfree(buffer);
336       errno=fh;
337       return -1;
338     }
339 #if defined(__UNIX__)
340 /*     flock(to,O_EXLOCK); */
341     w_log( LL_DEBUGZ, __FILE__ ":%u:copy_file()", __LINE__);
342     /* try to save file ownership if it is possible */
343     if (fchown(fh, st.st_uid, st.st_gid) != 0)
344         fchmod(fh, st.st_mode & 01777);
345     else
346         fchmod(fh, st.st_mode);
347 #endif
348     w_log( LL_DEBUGZ, __FILE__ ":%u:copy_file()", __LINE__);
349     fout = fdopen(fh, "wb");
350     if (fout == NULL) { fh=errno; nfree(buffer); fclose(fin); errno=fh; return -1; }
351 
352     while ((read = fread(buffer, 1, MOVE_FILE_BUFFER_SIZE, fin)) > 0)
353     {
354 	if (fwrite(buffer, 1, read, fout) != read)
355 	{   fh=errno;
356 	    fclose(fout); fclose(fin); remove(to); nfree(buffer);
357             errno=fh;
358             w_log( LL_DEBUGZ, __FILE__ ":%u:copy_file() failed", __LINE__);
359 	    return -1;
360 	}
361     }
362 
363     nfree(buffer);
364     if (ferror(fout) || ferror(fin))
365     {   fh=errno;
366 	fclose(fout);
367 	fclose(fin);
368         remove(to);
369         errno=fh;
370         w_log( LL_DEBUGZ, __FILE__ ":%u:copy_file() failed", __LINE__);
371 	return -1;
372     }
373     fclose(fin);
374     if (fclose(fout))
375     {   fh=errno;
376         remove(to);
377         errno=fh;
378         w_log( LL_DEBUGZ, __FILE__ ":%u:copy_file() failed", __LINE__);
379 	return -1;
380     }
381     ut.actime = st.st_atime;
382     ut.modtime = st.st_mtime;
383 # if defined(_MAKE_DLL_MVC_)
384     _utime(to, &ut);
385 # else
386     utime(to, &ut);
387 # endif
388 #elif defined (__NT__) && defined(USE_SYSTEM_COPY)
389     int rc = 0;
390 
391     if ( cmpfnames((char*)from,(char*)to) == 0 )
392         return 0;
393 
394     if(force_rewrite)
395       remove(to);            /* if CopyFile can't work file deleted..... */
396     else if(fexist(to)){
397       errno=EEXIST;
398       return -1;
399     }
400 
401     rc = CopyFile(from, to, FALSE);
402     if (rc == FALSE) {
403       remove(to);
404       return -1;
405     }
406 #elif defined (OS2) && defined(USE_SYSTEM_COPY)
407     USHORT rc;
408 
409 #ifdef HAVE_CMPFNAMES  /* check cmpfnames for all OS and remove this condition */
410     if ( cmpfnames((char*)from,(char*)to) == 0 )
411         return 0;
412 #endif
413 
414     if(force_rewrite)
415       remove(to);            /* if DosCopy can't work file deleted..... */
416     else if(fexist(to)){
417       errno=EEXIST;
418       return -1;
419     }
420 
421     rc = DosCopy((PSZ)from, (PSZ)to, 1);
422     if (rc) {
423       remove(to);
424       return -1;
425     }
426 #endif
427     w_log( LL_DEBUGZ, __FILE__ ":%u:copy_file() OK", __LINE__);
428     return 0;
429 }
430 
431 #ifndef cmdcall
432 
433 #if !defined(P_WAIT) && defined(_P_WAIT)
434 #define P_WAIT		_P_WAIT
435 #endif
436 
437 /* make parameters list for spawnvp(), spawnv()
438  * Return malloc()'ed array of pointers to difference parts of one string,
439  * 1st element points to string begin.
440  * Return NULL if argument is NULL
441  */
442 static char **mk_lst(const char *a)
443 {
444     char *p, *q, **list=NULL, end=0, num=0;
445 
446     if(!a){
447       w_log(LL_ERR, "NULL command line!");
448       return NULL;
449     }
450     while (*a && isspace(*a)) a++; /* Left spaces trim */
451     p=q=sstrdup(a);
452     while (*p && !end) {
453 	while (*q && !isspace(*q)) q++;
454 	if (*q=='\0') end=1;
455 	*q ='\0';
456 	list = (char **) srealloc(list, ++num*sizeof(char*));
457 	list[num-1]=(char*)p;
458 	if (!end) {
459 	    p=q+1;
460 	    while(isspace(*p)) p++;
461 	}
462 	q=p;
463     }
464     list = (char **) srealloc(list, (++num)*sizeof(char*));
465     list[num-1]=NULL;
466 
467     return list;
468 }
469 
470 int cmdcall(const char *cmd)
471 { int cmdexit=-1;
472   int signal;
473   char **list;
474 
475   if( (list = mk_lst(cmd)) != NULL ) {
476     w_log(LL_DEBUGV, "spawnvp(P_WAIT, %s, ...)", list[0] );
477 #if defined(__WATCOMC__) || defined(__MINGW32__)
478     cmdexit = spawnvp(P_WAIT, list[0], (const char * const *)list);
479 #else
480 #pragma message("spawnvp")
481 	cmdexit = spawnvp(P_WAIT, list[0], list);
482 #endif
483     nfree(list[0]);
484     nfree(list);
485   }
486 
487   if( (signal=(cmdexit & 0xff)) != 0 ) /* system error! */
488     w_log(LL_ERROR, "Command execute error (spawnwp()): signal %i (Run command '%s')", signal, cmd);
489   return (cmdexit & 0xFF00)>>8;   /* return code */
490 }
491 #else
492 /*
493 int cmdcall(const char *cmd)
494 { return system(cmd); }
495 */
496 #endif
497 
498 int lockFile(const char *lockfile, int advisoryLock)
499 {
500     int fh = -1;
501 
502     if(!lockfile)
503         return fh;
504 
505     if (advisoryLock > 0) {
506         while(advisoryLock > 0)
507         {
508             fh=open(lockfile,O_CREAT|O_RDWR,S_IREAD|S_IWRITE);
509             if (fh<0) {
510 /*                fprintf(stderr,"cannot open/create lock file: %s wait %d seconds\n",lockfile, advisoryLock);*/
511                 advisoryLock--;
512             } else {
513                 if (write(fh," ", 1)!=1) {
514 /*                    fprintf(stderr,"can't write to lock file! wait %d seconds\n", advisoryLock);*/
515                     close(fh);
516                     fh = -1;
517                     advisoryLock--;
518                 } else if (lock(fh,0,1)<0) {
519 /*                    fprintf(stderr,"lock file used by another process! %d seconds\n", advisoryLock);*/
520                     close(fh);
521                     fh = -1;
522                     advisoryLock--;
523                 }
524             }
525             if(fh < 0)
526                 sleep(1);
527             else
528                 break;
529         }
530     } else { /*  normal locking */
531         fh=open(lockfile, O_CREAT|O_RDWR|O_EXCL,S_IREAD|S_IWRITE);
532     }
533     if(fh < 0)
534     {
535 		fprintf(stderr,"cannot create new lock file: %s\n",lockfile);
536 		fprintf(stderr,"lock file probably used by another process! exit...\n");
537 	}
538     return fh;
539 }
540 
541 int FreelockFile(const char *lockfile, int fh)
542 {
543     if(fh > 0)
544     	close(fh);
545     if(lockfile)
546 	    remove(lockfile);
547 
548     return 0;
549 }
550 
551 /* converts decimal value to octal. useful for chmod() */
552 unsigned int dec2oct(unsigned int decimal)
553 {
554     char tmpstr[6];
555     unsigned int mode;
556 
557     mode = decimal;
558     sprintf(tmpstr, "%u", mode);
559     sscanf(tmpstr, "%o", &mode);
560     return mode;
561 }
562 
563 /*   Get the object name from the end of a full or partial pathname.
564     The GetFilenameFromPathname function gets the file (or directory) name
565     from the end of a full or partial pathname. Returns The file (or directory)
566     name: pointer to part of all original pathname.
567 
568 */
569 char    *GetFilenameFromPathname(const char* pathname)
570 {
571     char *fname = strrchr(pathname,PATH_DELIM);
572     if(fname)
573         fname++;
574     else
575         return (char*)pathname;
576     return fname;
577 }
578 
579 
580 /*  Get the object name from the end of a full or partial pathname (OS-independed).
581     This function gets the file (or directory) name from the end of a full
582     or partial pathname for any path style: UNIX, DOS or mixed (mixed style
583     may be used in Windows NT OS family).
584     Returns the file (or directory) name: pointer to part of all original pathname.
585 */
586 char *OS_independed_basename(const char *pathname)
587 { register char *fname=NULL, *pname=(char*)pathname;
588 
589   /* Process Unix-style, result to pathname */
590   fname = strrchr(pname,'/');
591   if( fname != NULL ) pname = ++fname;
592 
593   /* Process DOS-style */
594   fname = strrchr(pname,'\\');
595   if( fname != NULL ) ++fname;
596   else fname = pname;
597 
598   return fname;
599 }
600 
601 /* Return directory part of pathname (without filename, '/' or '\\' present at end)
602  * Return value is pointer to malloc'ed string;
603  * if pathname is filenfme without directory return current directory (./ or .\)
604  */
605 char    *GetDirnameFromPathname(const char* pathname)
606 {
607   char *sp=NULL, *rp=NULL;
608   register int lll;
609 
610   sp = strrchr(pathname,PATH_DELIM);
611   if( sp ){
612     sp++;
613     lll = sp-pathname;
614     rp = scalloc(lll+1,sizeof(char));
615     sstrncpy(rp, pathname, lll);
616   }else
617 #if PATH_DELIM=='/'
618     rp = sstrdup("./");
619 #else
620     rp = sstrdup(".\\");
621 #endif
622 
623   return rp;
624 }
625 
626 void fillCmdStatement(char *cmd, const char *call, const char *archiv, const char *file, const char *path)
627 {
628     const char *start, *tmp, *add;
629 #ifdef __WIN32__
630     char *p;
631 #endif
632     char fullarch[256];
633     char fullpath[256];
634 
635 #ifdef __WIN32__
636     GetFullPathName(archiv, sizeof(fullarch), fullarch, &p);
637     if(*path)
638     GetFullPathName(path, sizeof(fullpath), fullpath, &p);
639 #else
640     strnzcpy(fullpath,path, 255);
641     strnzcpy(fullarch,archiv, 255);
642 #endif
643 
644     *cmd = '\0';  start = NULL;
645     for (tmp = call; (start = strchr(tmp, '$')) != NULL; tmp = start + 2) {
646         switch(*(start + 1)) {
647         case 'a': add = fullarch; break;
648         case 'p': add = fullpath; break;
649         case 'f': add = file; break;
650         default:
651             strncat(cmd, tmp, (size_t) (start - tmp + 1));
652             start--; continue;
653         };
654         strnzcat(cmd, tmp, (size_t) (start - tmp + 1));
655         strcat(cmd, add);
656     };
657     strcat(cmd, tmp);
658 }
659