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