1 /* datei.cc
2  * This file belongs to Worker, a file manager for UN*X/X11.
3  * Copyright (C) 2001-2020 Ralf Hoffmann.
4  * You can contact me at: ralf@boomerangsworld.de
5  *   or http://www.boomerangsworld.de/worker
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "datei.h"
23 #include "aguix/lowlevelfunc.h"
24 #include "wconfig.h"
25 #include "grouphash.h"
26 #include "aguix/mutex.h"
27 #include "aguix/utf8.hh"
28 #include "nwc_os.hh"
29 #include "nwc_path.hh"
30 #include "worker.h"
31 
Datei()32 Datei::Datei()
33 {
34   fd = -1;
35   error=0;
36   iseof = false;
37 
38   sectiondepth = 0;
39 }
40 
~Datei()41 Datei::~Datei()
42 {
43   if ( fd >= 0 ) close();
44 }
45 
open(const char * name,const char * mode)46 int Datei::open( const char *name, const char *mode )
47 {
48   int m;
49   mode_t p;
50 
51   if ( name == NULL ) return 1;
52   if ( mode == NULL ) return 1;
53 
54   if ( strlen( mode ) < 1 ) return 1;  // wrong mode
55 
56   //TODO: Currently "b" in mode will fail open in any case
57 
58   p = 0;
59   if ( strcasecmp( mode, "r" ) == 0 ) {
60     m = O_RDONLY;
61   } else if ( strcasecmp( mode, "r+" ) == 0 ) {
62     m = O_RDWR;
63   } else if ( strcasecmp( mode, "w" ) == 0 ) {
64     m = O_WRONLY | O_CREAT | O_TRUNC;
65     p = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
66   } else if ( strcasecmp( mode, "w+" ) == 0 ) {
67     m = O_RDWR | O_CREAT | O_TRUNC;
68     p = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
69   } else if ( strcasecmp( mode, "a" ) == 0 ) {
70     m = O_WRONLY | O_CREAT | O_APPEND;
71     p = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
72   } else if ( strcasecmp( mode, "a+" ) == 0 ) {
73     m = O_RDWR | O_CREAT | O_APPEND;
74     p = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
75   } else {
76     return 1;
77   }
78 
79   p = p & ~Worker::getUMask();
80 
81   fd = ::worker_open( name, m, p );
82   if ( fd < 0 ) return 1;
83   iseof = false;
84   return 0;
85 }
86 
close()87 int Datei::close()
88 {
89   int res = -1;
90 
91   if ( fd < 0 ) return res;
92   res = ::worker_close( fd );
93   fd = -1;
94   iseof = false;
95 
96   if ( res != 0 ) error++;
97   return res;
98 }
99 
putUChar(unsigned char value)100 int Datei::putUChar(unsigned char value)
101 {
102   if ( fd < 0 ) return -1;
103   int x;
104   x = putCharacter( value );
105   if(x!=EOF) return 1;
106   return 0;
107 }
108 
putUShort(unsigned short int value)109 int Datei::putUShort(unsigned short int value)
110 {
111   if ( fd < 0 ) return -1;
112   int x;
113   x = putCharacter( value >> 8 );
114   if(x==EOF) return 0;
115   x = putCharacter( value & 0xff );
116   if(x==EOF) return 1;
117   return 2;
118 }
119 
putInt(int value)120 int Datei::putInt(int value)
121 {
122   if ( fd < 0 ) return -1;
123   int x,tv;
124   if(value<0) {
125     x = putCharacter( 1 );
126     if(x==EOF) return 0;
127   } else {
128     x = putCharacter( 0 );
129     if(x==EOF) return 0;
130   }
131   tv=abs(value);
132   int i;
133   for(i=0;i<4;i++) {
134     x = putCharacter( tv & 0xff );
135     if(x==EOF) return i+1;
136     tv>>=8;
137   }
138   return i+1;
139 }
140 
getInt()141 int Datei::getInt()
142 {
143   if ( fd < 0 ) return -1;
144   int vz,x,value;
145   vz = getCharacter();
146   if(vz<0) {
147     error++;
148     return 0;
149   }
150   value=0;
151   for(int i=0;i<4;i++) {
152     x = getCharacter();
153     if(x==EOF) {
154       error++;
155       return 0;
156     }
157     value+=(x<<(8*i));
158   }
159   if(vz==1) value*=-1;
160   return value;
161 }
162 
putULong(unsigned long value)163 int Datei::putULong(unsigned long value)
164 {
165   if ( fd < 0 ) return -1;
166   int x;
167   x = putCharacter( ( value & 0xff000000 ) >> 24 );
168   if(x==EOF) return 0;
169   x = putCharacter( ( value & 0xff0000 ) >> 16 );
170   if(x==EOF) return 1;
171   x = putCharacter( ( value & 0xff00 ) >> 8 );
172   if(x==EOF) return 2;
173   x = putCharacter( value & 0xff );
174   if(x==EOF) return 3;
175   return 4;
176 }
177 
putString(const char * str1)178 int Datei::putString( const char *str1 )
179 {
180   int x, l;
181 
182   if ( fd < 0 ) return -1;
183   if ( str1 == NULL ) return -1;
184 
185   l = strlen( str1 );
186   x = worker_write( fd, str1, l );
187   if ( x < 0 ) {
188     error++;
189     return -2;
190   } else if ( x != l ) {
191     error++;
192   }
193   return x;
194 }
195 
getUChar()196 unsigned char Datei::getUChar()
197 {
198   if ( fd < 0 ) {
199     error++;
200     return 0;
201   }
202   int x;
203   x = getCharacter();
204   if(x==EOF) {
205     error++;
206     return 0;
207   }
208   return (unsigned char) x;
209 }
210 
getCharAsInt()211 int Datei::getCharAsInt()
212 {
213   if ( fd < 0 ) {
214     error++;
215     return 0;
216   }
217   return getCharacter();
218 }
219 
getUShort()220 unsigned short Datei::getUShort()
221 {
222   if ( fd < 0 ) {
223     error++;
224     return 0;
225   }
226   int x;
227   unsigned short int a;
228   x = getCharacter();
229   if(x==EOF) {
230     error++;
231     return 0;
232   }
233   a=((unsigned int)x)*0x100;
234   x = getCharacter();
235   if(x==EOF) {
236     error++;
237     return 1;
238   }
239   a+=(unsigned int)x;
240   return a;
241 }
242 
getULong()243 unsigned long Datei::getULong()
244 {
245   if ( fd < 0 ) {
246     error++;
247     return 0;
248   }
249   int x;
250   unsigned long a;
251   x = getCharacter();
252   if(x==EOF) {
253     error++;
254     return 0;
255   }
256   a=((unsigned int)x)*0x1000000;
257   x = getCharacter();
258   if(x==EOF) {
259     error++;
260     return 1;
261   }
262   a+=((unsigned int)x)*0x10000;
263   x = getCharacter();
264   if(x==EOF) {
265     error++;
266     return 2;
267   }
268   a+=((unsigned int)x)*0x100;
269   x = getCharacter();
270   if(x==EOF) {
271     error++;
272     return 3;
273   }
274   a+=(unsigned int)x;
275   return a;
276 }
277 
getString(int count)278 char *Datei::getString(int count)
279 {
280   if ( fd < 0 ) return NULL;
281   unsigned char *tstr,*tstr2;
282   int ch;
283   long x;
284   long size;
285   if(count==-1) {
286     /* lesen bis NULL-Byte */
287     size=1024;
288     tstr=(unsigned char*)_allocsafe(size);
289     x=0;
290     do {
291       ch = getCharacter();
292       if(ch!=EOF) {
293         tstr[x++]=(unsigned char)ch;
294       } else {
295         tstr[x++]=0;
296         break;
297       }
298       if(x==size) {
299         size*=2;
300         tstr2=(unsigned char*)_allocsafe(size);
301         if(tstr2==NULL) break;
302         strcpy((char*)tstr2,(char*)tstr);
303         _freesafe(tstr);
304         tstr=tstr2;
305       }
306     } while(ch!=0);
307   } else {
308     tstr=(unsigned char*)_allocsafe(count+1);
309     if(tstr!=NULL) {
310       for(x=0;x<count;x++) {
311 	ch = getCharacter();
312         tstr[x]=(unsigned char)ch;
313       }
314       tstr[count]=0;
315     }
316   }
317   return (char*)tstr;
318 }
319 
getString(char * buf,int count)320 int Datei::getString(char *buf,int count)
321 {
322   buf[0]=0;
323   char *str=getString(count);
324   strncpy(buf,str,count);
325   buf[count]=0;
326   _freesafe(str);
327   return strlen(buf);
328 }
329 
fileExists(const char * name)330 bool Datei::fileExists(const char *name)
331 {
332   worker_struct_stat stbuf;
333   if ( worker_lstat( name, &stbuf ) != 0 ) return false;
334   else {
335     /* kann auch verzeichnis sein */
336 //    if(S_ISDIR(stbuf.st_mode)) return false;
337   }
338   return true;
339 }
340 
fileExistsExt(const char * name)341 Datei::d_fe_t Datei::fileExistsExt(const char*name)
342 {
343   worker_struct_stat stbuf;
344   d_fe_t return_value;
345   if ( worker_stat( name, &stbuf ) != 0 ) return_value = D_FE_NOFILE;
346   else {
347     if(S_ISDIR(stbuf.st_mode)) return_value=D_FE_DIR;
348     else return_value=D_FE_FILE;
349     if(S_ISLNK(stbuf.st_mode)) return_value=D_FE_LINK;
350   }
351   return return_value;
352 }
353 
lfileExistsExt(const char * name)354 Datei::d_fe_t Datei::lfileExistsExt(const char*name)
355 {
356   worker_struct_stat stbuf;
357   d_fe_t return_value;
358   if ( worker_lstat( name, &stbuf ) != 0 ) return_value = D_FE_NOFILE;
359   else {
360     if(S_ISDIR(stbuf.st_mode)) return_value=D_FE_DIR;
361     else return_value=D_FE_FILE;
362     if(S_ISLNK(stbuf.st_mode)) return_value=D_FE_LINK;
363   }
364   return return_value;
365 }
366 
seek(long offset,int mode)367 void Datei::seek(long offset,int mode)
368 {
369   if ( fd >= 0 ) worker_lseek( fd, offset, mode );
370 }
371 
getOwnerStringLen(uid_t user,gid_t gr)372 int getOwnerStringLen(uid_t user,gid_t gr)
373 {
374   char *tstr;
375   #ifdef DEBUG
376 //  printf("entering getOwnerStringLen\n");
377   #endif
378   int len=0;
379   int ost=wconfig->getOwnerstringtype();
380   #ifdef DEBUG
381 //  printf("  getUserNameS\n");
382   #endif
383   tstr=ugdb->getUserNameS(user);
384   if(tstr!=NULL) len+=strlen(tstr);
385   len+=(ost==1)?1:3;
386   #ifdef DEBUG
387 //  printf("  getGroupNameS\n");
388   #endif
389   tstr=ugdb->getGroupNameS(gr);
390   if(tstr!=NULL) len+=strlen(tstr);
391   #ifdef DEBUG
392 //  printf("leaving getOwnerStringLen\n");
393   #endif
394   return len;
395 }
396 
getOwnerString(uid_t user,gid_t mygroup)397 char* getOwnerString(uid_t user,gid_t mygroup)
398 {
399   int len=0;
400   char *tstr1,*tstr2;
401   int ost=wconfig->getOwnerstringtype();
402 
403   tstr1=ugdb->getUserNameS(user);
404   tstr2=ugdb->getGroupNameS(mygroup);
405   if(tstr1!=NULL) len+=strlen(tstr1);
406   len+=(ost==1)?1:3;
407   if(tstr2!=NULL) len+=strlen(tstr2);
408   char *str=(char*)_allocsafe((len+2)*sizeof(char));
409   str[0]=0;
410   if(tstr1!=NULL) strcat(str,tstr1);
411   strcat(str,(ost==1)?".":" @ ");
412   if(tstr2!=NULL) strcat(str,tstr2);
413   return str;
414 }
415 
overreadChunk()416 int Datei::overreadChunk()
417 {
418   int chunksize=getInt();
419   while(chunksize>0) {
420     getUChar();
421     chunksize--;
422   }
423   return 0;
424 }
425 
getIntSize()426 int Datei::getIntSize()
427 {
428   return 5;
429 }
430 
getUCharSize()431 int Datei::getUCharSize()
432 {
433   return 1;
434 }
435 
getUShortSize()436 int Datei::getUShortSize()
437 {
438   return 2;
439 }
440 
getULongSize()441 int Datei::getULongSize()
442 {
443   return 4;
444 }
445 
errors()446 long Datei::errors()
447 {
448   long e=error;
449   error=0;
450   return e;
451 }
452 
getFilenameFromPath(const char * path)453 char *Datei::getFilenameFromPath(const char*path)
454 {
455   char *tstr;
456   int pos;
457   if(path==NULL) return NULL;
458   pos=strlen(path)-2;  // -2 because a name contains min. 1 character
459                        // but it can be a slash so skiping it
460   while(pos>=0) {
461     if(path[pos]=='/') break;
462     pos--;
463   }
464   if(pos>=0) tstr=dupstring(&path[pos+1]);
465   else tstr=dupstring(path);
466   return tstr;
467 }
468 
getNameWithoutExt(const char * name)469 char *Datei::getNameWithoutExt(const char *name)
470 {
471   char *tstr;
472   int pos,len;
473   bool found=false;
474   if(name==NULL) return NULL;
475   len=strlen(name);
476   pos=len-1;
477   while(pos>=0) {
478     if((name[pos]=='/')&&(pos<(len-1))) break;
479     if(name[pos]=='.') {
480       found=true;
481       break;
482     }
483     pos--;
484   }
485   if(found==false) tstr=dupstring(name);
486   else {
487     tstr=(char*)_allocsafe(pos+1);
488     if(pos>0) strncpy(tstr,name,pos);
489     tstr[pos]=0;
490   }
491   return tstr;
492 }
493 
494 /**
495  * function returns name of tmp file in user's worker directory
496  * file will be created
497  * It can however return NULL if after a certain number of
498  * retries no tmp file couldn't be created
499  * THREADSAFE
500  */
createTMPName(const char * infix,const char * suffix)501 char *Datei::createTMPName( const char *infix,
502                             const char *suffix )
503 {
504     char *returnstr = NULL, buffer[ 2 * A_BYTESFORNUMBER( int ) ];
505     int rannr;
506     static int lfdnr = 0;
507     int tfd;
508     static MutEx lock;
509 
510     if ( infix == NULL ) infix = "";
511     if ( suffix == NULL ) suffix = "";
512 
513     lock.lock();
514     for( int retry = 0; retry < 1000; retry++ ) {
515         rannr=rand();
516         sprintf( buffer, "%d%d", lfdnr++, rannr );
517         returnstr = (char*)_allocsafe( strlen( worker_tmpdir() ) +
518                                        strlen( "/worker" ) +
519                                        strlen( infix ) + strlen( buffer ) +
520                                        strlen( suffix ) + 1 );
521         sprintf( returnstr, "%s/worker%s%s%s", worker_tmpdir(), infix, buffer, suffix );
522         if(Datei::lfileExistsExt(returnstr)==Datei::D_FE_NOFILE) {
523             tfd = ::worker_open( returnstr, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR );
524             if ( tfd >= 0 ) {
525                 ::worker_close( tfd );
526                 worker_chmod( returnstr, S_IRUSR | S_IWUSR );
527                 break;
528             }
529         }
530         _freesafe(returnstr);
531         returnstr = NULL;
532     }
533     lock.unlock();
534     return returnstr;
535 }
536 
createTMPHardlink(const std::string & name)537 std::string Datei::createTMPHardlink( const std::string &name )
538 {
539     std::string returnstr;
540     int rannr;
541     static int lfdnr = 0;
542     static MutEx lock;
543 
544     lock.lock();
545 
546     for( int retry = 0; retry < 1000; retry++ ) {
547         rannr = rand();
548 
549         returnstr = AGUIXUtils::formatStringToString( "%s/workerlnk%s%d%d",
550                                                       worker_tmpdir(),
551                                                       "",
552                                                       lfdnr++, rannr );
553         if ( Datei::lfileExistsExt( returnstr.c_str() ) == Datei::D_FE_NOFILE ) {
554             if ( worker_link( name.c_str(),
555                               returnstr.c_str() ) == 0 ) {
556                 break;
557             } else {
558                 if ( errno != EEXIST ) {
559                     returnstr = "";
560                     break;
561                 }
562             }
563         }
564         returnstr = "";
565     }
566 
567     lock.unlock();
568 
569     return returnstr;
570 }
571 
putStringExt(const char * format,const char * str)572 int Datei::putStringExt(const char *format,const char *str)
573 {
574   int written, size;
575   char *tstr;
576 
577   if ( fd < 0 ) return -1;
578   if ( format == NULL ) return -1;
579   if ( str == NULL ) return -1;
580 
581   size = strlen( format ) + strlen( str ) + 1;
582   tstr = (char*)_allocsafe( size );
583   while ( 1 ) {
584     written = snprintf( tstr, size, format, str );
585     if ( ( written > -1 ) && ( written < size ) ) break;
586 
587     //TODO: in case of an error we try to use more space
588     //      but if for whatever reason does not work at all
589     //      we should perhaps not end the program (because)
590     //      alloc will fail) but only quit this function
591     size *= 2;
592     _freesafe( tstr );
593     tstr = (char*)_allocsafe( size );
594   }
595   putString( tstr );
596   _freesafe( tstr );
597   return 0;
598 }
599 
shrinkFilename(const char * str,int maxlength,AWidth & lencalc)600 char *Datei::shrinkFilename( const char *str, int maxlength, AWidth &lencalc )
601 {
602   char *tstr = NULL;
603   int len, len1;
604   bool stopnow;
605   const char *lastl, *lastr, *startl, *startr, *str1;
606   const char *filename;
607   int dddlen;
608   int l1, l2, l3, real_width;
609   std::string tstr1;
610 
611   if ( str == NULL )
612     return NULL;
613 
614   // test if the str will fit in maxlength
615   len = strlen( str );
616   if( ( lencalc.getWidth( str, len ) <= maxlength ) || ( len <= 5 ) )
617     return dupstring( str );
618 
619   dddlen = lencalc.getWidth( "..."  );
620 
621   // now we really have to do something
622 
623   // first find the most important part, the filename
624   // we will also take the beginning slash (if exists) to show the user
625   // the filename is not shorten
626   // first test: is there any slash
627   // if not then shorten the str with lowlevelfunc shrinkstring
628   // if:
629   //   filename starts at the last slash or the next to last
630   //     if the str ends with a slash
631   //   if this is longer then maxlength-5 (atleast 2 chars at the beginning and "...")
632   //     then just do a shringstring because the filename will be shorten in any case
633   //   otherwise:
634   //     find the first dirname from left
635   //     if it will fit into the string, add it
636   //     otherwise take the previous string and stop
637   //     do the same with the first dirname from right
638   //   specialcase: filename is shorter then maxlength-5
639   //     but the first dir doesnt fit, then use as much chars from it
640   //     to fill maxlength
641 
642   int right_pos = len; // this is the 0 byte
643   UTF8::movePosToPrevChar( str, right_pos );  // to last character
644   UTF8::movePosToPrevChar( str, right_pos );  /* to skip a final / (if exists)
645                                                  since a filename is atleast 1 char this would
646                                                  point in the worst case at the beginning slash */
647   str1 = str + right_pos;
648 
649   // now search for the slash backwards
650 
651   while ( *str1 != '/' ) {
652     str1--;
653     if ( str1 < str ) break;
654   }
655 
656   if ( str1 < str ) {
657     // no slash (or only a final one)
658     return shrinkstring( str, maxlength, lencalc );
659   } else {
660     // found filename
661     // we will also take the slash into it
662     filename = str1;
663 
664     if ( lencalc.getWidth( filename ) + lencalc.getWidth( "/.../" ) > maxlength )
665       return shrinkstring( str, maxlength, lencalc );
666 
667     // enough room for some additional chars
668     startl = str;
669     startr = filename;
670     lastl = str;      // this are the last positions that will fit into maxlength
671     lastr = filename;
672 
673     if ( startl[0] == '/' )  // starting slash => skip
674       startl++;
675 
676     // startr is at the start filename and so points to a slash
677     // but because we want to prefer the first dir from left, leave it so
678 
679     for ( stopnow = false; ; ) {
680       // remember: there is a slash before the filename
681       // so this will stop there (or before)
682       while ( *startl != '/' ) {
683         startl++;
684       }
685       // this could run over the left
686       while ( *startr != '/' ) {
687         startr--;
688         if ( startr < str ) break;
689       }
690 
691       if ( startl > startr ) {
692         // since startr is before startl both found the same mid dir
693         // this dir will not fit in maxlength because then everything would fit
694         // but this cannot be because of previous checks
695         // just take lastl und lastr to build new string
696         stopnow = true;
697       } else {
698         // test if startl and/or startr will fit
699         // otherwise stop
700 
701         // get the number of bytes up to including startl
702         right_pos = startl - str;
703         UTF8::movePosToNextChar( str, right_pos );
704         len1 = right_pos;
705         /* len1 is actually ( startl - str + 1 ) since startl
706            points to the slash but this it's generic
707            the same goes for the next two lastl calculations */
708 
709 	l1 = lencalc.getWidth( str, len1 );
710 	l2 = lencalc.getWidth( lastr );
711 
712         if ( ( l1 + dddlen + l2 ) <= maxlength ) {
713           // new dir at the left will fit
714           lastl = startl;
715         }
716 
717         // get the number of bytes up to including lastl
718         right_pos = lastl - str;
719         UTF8::movePosToNextChar( str, right_pos );
720         len1 = right_pos;
721 
722 	l1 = lencalc.getWidth( str, len1 );
723 	l2 = lencalc.getWidth( startr );
724 
725         if ( ( l1 + dddlen + l2 ) <= maxlength ) {
726           // new dir at the right will fit
727           lastr = startr;
728         }
729 
730         if ( ( lastl != startl ) && ( lastr != startr ) ) {
731           // nothing has changed so stop
732           stopnow = true;
733         }
734       }
735 
736       if ( stopnow == true ) {
737         // time to stop
738         if ( lastl == str ) {
739           // no fitting dirs found
740           // so take as much as possibly
741 
742 	  l1 = lencalc.getWidth( filename );
743 	  l1 += dddlen;
744 	  l2 = maxlength - l1;
745 
746 	  if ( l2 > 0 ) {
747 	    l3 = lencalc.getStrlen4Width( str, l2, &real_width );
748 
749 	    tstr1 = "";
750 	    tstr1.append( str, l3 );
751 	    tstr1 += "...";
752 	    tstr1 += filename;
753 	    tstr = dupstring( tstr1.c_str() );
754 	  } else {
755 	    tstr = shrinkstring( str, maxlength, lencalc );
756 	  }
757         } else {
758           // get the number of bytes up to including lastl
759           right_pos = lastl - str;
760           UTF8::movePosToNextChar( str, right_pos );
761           len1 = right_pos;
762 
763 	  tstr1 = "";
764 	  tstr1.append( str, len1 );
765 	  tstr1 += "...";
766 	  tstr1 += lastr;
767 	  if ( lencalc.getWidth( tstr1.c_str() ) > maxlength ) {
768 	    tstr = shrinkstring( str, maxlength, lencalc );
769 	  } else {
770 	    tstr = dupstring( tstr1.c_str() );
771 	  }
772         }
773         break;
774       }
775 
776       startl++;
777       startr--;
778     }
779   }
780 
781   return tstr;
782 }
783 
putLine(const char * str)784 int Datei::putLine( const char *str )
785 {
786   int x, y, l;
787 
788   if ( str == NULL ) return -1;
789   if ( fd < 0 ) return -1;
790 
791   l = strlen( str );
792   x = worker_write( fd, str, l );
793   if ( x < 0 ) {
794     error++;
795     return -2;
796   } else if ( x != l ) {
797     error++;
798   }
799 
800   y = worker_write( fd, "\n", 1 );
801   if ( y < 0 ) {
802     error++;
803     return -2;
804   } else if ( y != 1 ) {
805     error++;
806   }
807 
808   return x + y;
809 }
810 
getLine()811 char *Datei::getLine()
812 {
813   if ( fd < 0 ) return NULL;
814   unsigned char *tstr,*tstr2;
815   int ch;
816   long x;
817   long size;
818 
819   size=1024;
820   tstr = (unsigned char*)_allocsafe( size );
821   x = 0;
822   do {
823     ch = getCharacter();
824     if ( ch != EOF ) {
825       tstr[ x++ ] = (unsigned char)ch;
826     } else {
827       tstr[ x++ ] = 0;
828       break;
829     }
830     if ( x == size ) {
831       size *= 2;
832       tstr2 = (unsigned char*)_allocsafe( size );
833       if ( tstr2 == NULL ) break;
834       memcpy( (char*)tstr2, (char*)tstr, x );
835       _freesafe( tstr );
836       tstr = tstr2;
837     }
838   } while ( ( ch != 0 ) && ( ch != '\n' ) );
839   if ( tstr[ x - 1 ] == '\n' ) tstr[ x - 1 ] = '\0';
840   return (char*)tstr;
841 }
842 
isEOF()843 bool Datei::isEOF()
844 {
845   if ( fd < 0 ) return true;
846   return iseof;
847 }
848 
fileSize(const char * name)849 loff_t Datei::fileSize( const char *name )
850 {
851   worker_struct_stat stbuf;
852   loff_t return_value;
853 
854   if ( worker_stat( name, &stbuf ) != 0 ) return_value = -1;
855   else {
856     return_value = stbuf.st_size;
857   }
858   return return_value;
859 }
860 
861 /*
862  * getRelativePath
863  *
864  * will return relative path for source in destdir
865  * in other words: destdir/returnvalue will be the same fileentry
866  * as source
867  */
getRelativePath(const char * source,const char * destdir,bool use_realpath)868 char *Datei::getRelativePath( const char *source, const char *destdir, bool use_realpath )
869 {
870   int startpos, curpos, mode, i;
871   char *mysource, *mydestdir;
872   std::string erg;
873 
874   if ( ( source == NULL ) || ( destdir == NULL ) ) return NULL;
875 
876   /* basicly it works this way:
877      first remove same directories from the beginning
878      then for each directory in destdir add ".." before the source
879 
880      because this code cannot handle .. and . in the paths
881      I remove them with HandlePath
882      It's not a big deal to handle these strings too (by ignoring
883      "." and removing an added "../" for ".." but this way this function
884      keeps small and clean :-)
885    */
886 
887   if ( use_realpath ) {
888       mysource = NWC::Path::handlePath( source );
889       mydestdir = NWC::Path::handlePath( NWC::Path::get_real_path( destdir ).c_str() );
890 
891       std::string real_destdir = NWC::Path::get_real_path( destdir );
892       if ( real_destdir.empty() ) {
893           mydestdir = NWC::Path::handlePath( destdir );
894       } else {
895           mydestdir = NWC::Path::handlePath( real_destdir.c_str() );
896       }
897   } else {
898       mysource = NWC::Path::handlePath( source );
899       mydestdir = NWC::Path::handlePath( destdir );
900   }
901 
902   for ( i = strlen( mysource ) - 1; i >= 0; i-- ) {
903     if ( mysource[i] == '/' ) mysource[i] = '\0';
904     else break;
905   }
906   for ( i = strlen( mydestdir ) - 1; i >= 0; i-- ) {
907     if ( mydestdir[i] == '/' ) mydestdir[i] = '\0';
908     else break;
909   }
910 
911   if ( ( strlen( mysource ) < 1 ) || ( strlen( mydestdir ) < 1 ) ) {
912     _freesafe( mysource );
913     _freesafe( mydestdir );
914     return NULL;
915   }
916 
917   startpos = curpos = 0;
918   erg = "";
919   mode = 0;
920 
921   // I just parse mydestdir and add "../" for every /
922   // but because I want to remove same basedir
923   // I will also parse the source string
924   for (;;) {
925     // the end of mydestdir is the only way to break this loop
926     if ( mydestdir[curpos] == '\0' ) {
927       // add "../" because destdir is a dir (as the name says)
928       // then add the rest of mysource
929 #if 0
930       // this code is correct!
931       // but it will create results the user
932       // doesn't expect (because it's too complicated)
933       // for example
934       // linking /tmp/file1 into /tmp would
935       // would result in ../tmp/file1 which is 100%
936       // correct but the user expect just file1 as
937       // relative path and that's what the #else part
938       // checks
939       erg += "../";
940       erg += &mysource[startpos];
941 #else
942       if ( ( mode == 0 ) && ( mysource[ curpos ] == '/' ) ) {
943         // mydestdir ended but we are still in "same character" mode
944         // and the source also ends as an dir so there is no need
945         // for any "../"
946         // read the above comment !
947         erg += &mysource[ curpos + 1 ];
948       } else {
949         erg += "../";
950         erg += &mysource[startpos];
951       }
952 #endif
953       break;
954     }
955     switch ( mode ) {
956       case 1:
957         if ( mydestdir[ curpos++ ] == '/' ) {
958           erg += "../";
959         }
960         break;
961       default:
962         if ( mysource[ curpos ] == '\0' ) mode = 1;  // no curpos change!
963         else if ( ( mysource[ curpos ] == '/' ) &&
964                   ( mydestdir[ curpos ] == '/' ) ) {
965           // new directory, old one was identically
966           // so set new start
967           startpos = ++curpos;
968         } else if ( mysource[ curpos ] == mydestdir[ curpos ] ) {
969           // same character => keep in this mode
970           curpos++;
971         } else mode = 1;  // no cursorchange
972         break;
973     }
974   }
975   _freesafe( mysource );
976   _freesafe( mydestdir );
977   if ( erg.length() < 1 ) return NULL;
978   return dupstring( erg.c_str() );
979 }
980 
getRelativePathExt(const char * source,const char * destdir,bool use_realpath)981 char *Datei::getRelativePathExt( const char *source,
982                                  const char *destdir,
983                                  bool use_realpath )
984 {
985     char *erg[2] = { NULL, NULL };
986 
987     if ( ! source || ! destdir ) return NULL;
988 
989     erg[0] = getRelativePath( source, destdir, use_realpath );
990     if ( erg[0] == NULL ) {
991         return NULL;
992     }
993 
994     if ( use_realpath ) {
995         std::string real_source = NWC::Path::get_real_path( source );
996         if ( ! real_source.empty() ) {
997             erg[1] = getRelativePath( real_source.c_str(),
998                                       destdir,
999                                       use_realpath );
1000         }
1001     }
1002 
1003     if ( erg[1] == NULL ) {
1004         return erg[0];
1005     }
1006 
1007     // if the relative path for the real_source is shorter, take this one
1008     if ( strlen( erg[1] ) < strlen( erg[0] ) ) {
1009         _freesafe( erg[0] );
1010         return erg[1];
1011     }
1012 
1013     _freesafe( erg[1] );
1014     return erg[0];
1015 }
1016 
putCharacter(int c)1017 int Datei::putCharacter( int c )
1018 {
1019   unsigned char cf[1];
1020   int count;
1021 
1022   if ( fd < 0 ) return EOF;
1023 
1024   cf[0] = (unsigned char)c;
1025 
1026   count = worker_write( fd, cf, 1 );
1027   if ( count < 1 ) {
1028     error++;
1029     return EOF;
1030   }
1031   return cf[0];
1032 }
1033 
getCharacter()1034 int Datei::getCharacter()
1035 {
1036   unsigned char cf[1];
1037   int count;
1038 
1039   if ( fd < 0 ) return EOF;
1040 
1041   count = ::worker_read( fd, cf, 1 );
1042 
1043   if ( count < 1 ) {
1044     iseof = true;
1045     return EOF;
1046   }
1047   return (int)( cf[0] );
1048 }
1049 
copyfile(const char * dest,const char * source,CopyfileProgressCallback * progress_callback)1050 int Datei::copyfile( const char *dest, const char *source,
1051                      CopyfileProgressCallback *progress_callback )
1052 {
1053   int fdw,fdr;
1054   bool useregcopy, cancel, follow_symlinks, isCorrupt;
1055   ssize_t readbytes,writebytes;
1056 #define BUFSIZE ( 4 * 1024 )
1057   char *buf[BUFSIZE];
1058   worker_struct_stat st, dst;
1059   int erg;
1060   loff_t total_bytes = 0, bytes_copied = 0;
1061 
1062   if ( ( dest == NULL ) || ( source == NULL ) ) return 1;
1063 
1064   follow_symlinks = false;
1065   isCorrupt = false;
1066   cancel = false;
1067 
1068   // get stat for file
1069   if ( worker_lstat( source, &st ) != 0 ) return 1;
1070 
1071   // get stat for destination if link (will fail for corrupt links)
1072   // but currently I don't handle them anyway
1073   if ( worker_stat( source, &dst ) != 0 ) return 1;
1074 
1075   useregcopy = false;
1076   if ( S_ISREG( st.st_mode ) ) {
1077       useregcopy = true;
1078       total_bytes = st.st_size;
1079   } else if ( ( S_ISLNK( st.st_mode ) ) &&
1080 	    ( follow_symlinks == true ) &&
1081 	    ( isCorrupt == false ) &&
1082               ( S_ISREG( dst.st_mode ) ) ) {
1083       useregcopy = true;
1084       total_bytes = dst.st_size;
1085   }
1086 
1087   if ( useregcopy == false ) return 1;
1088 
1089   fdr = ::worker_open( source, O_RDONLY, 0 );
1090   if ( fdr == -1 ) {
1091     // can't open inputfile
1092     return 1;
1093   }
1094 
1095   fdw = ::worker_open( dest, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR );
1096   if ( fdw == -1 ) {
1097     ::worker_close( fdr );
1098     return 1;
1099   }
1100 
1101   writebytes = -1;
1102   erg = 0;
1103   do {
1104     readbytes = ::worker_read( fdr, buf, BUFSIZE );
1105     if ( readbytes < 0 ) {
1106       // error while reading
1107       erg = 1;
1108       break;
1109     } else if ( readbytes == 0 ) {
1110       break;
1111     } else {
1112       writebytes = worker_write( fdw, buf, readbytes );
1113       if ( writebytes != readbytes ) {
1114 	// something went wrong
1115 	erg = 1;
1116 	break;
1117       }
1118 
1119       bytes_copied += writebytes;
1120 
1121       if ( progress_callback != NULL ) {
1122           progress_callback->progress_callback( bytes_copied,
1123                                                 total_bytes );
1124       }
1125     }
1126   } while ( ( readbytes > 0 ) &&
1127 	    ( cancel == false ) );
1128 
1129   ::worker_close( fdr );
1130   if ( ::worker_close( fdw ) != 0 ) {
1131     // error while closing -> incomplete copy
1132     erg = 1;
1133   }
1134   return erg;
1135 #undef BUFSIZE
1136 }
1137 
configPutPair(const char * id,const char * info)1138 void Datei::configPutPair( const char *id, const char *info )
1139 {
1140   std::string str1;
1141   int x;
1142 
1143   if ( ( id == NULL ) || ( info == NULL ) ) return;
1144   str1 = "";
1145   for ( x = 0; x < sectiondepth; x++ ) str1 += "\t";
1146   str1 += id;
1147   str1 += " = ";
1148   str1 += info;
1149   str1 += ";";
1150   putLine( str1.c_str() );
1151 }
1152 
configPutPairNum(const char * id,int num)1153 void Datei::configPutPairNum( const char *id, int num )
1154 {
1155   std::string str1;
1156   char buf[A_BYTESFORNUMBER( int )];
1157   int x;
1158 
1159   if ( id == NULL ) return;
1160   str1 = "";
1161   for ( x = 0; x < sectiondepth; x++ ) str1 += "\t";
1162   sprintf( buf, "%d", num );
1163   str1 += id;
1164   str1 += " = ";
1165   str1 += buf;
1166   str1 += ";";
1167   putLine( str1.c_str() );
1168 }
1169 
configPutPairNum(const char * id,int num1,int num2)1170 void Datei::configPutPairNum( const char *id, int num1, int num2 )
1171 {
1172   std::string str1;
1173   char buf[A_BYTESFORNUMBER( int )];
1174   int x;
1175 
1176   if ( id == NULL ) return;
1177   str1 = "";
1178   for ( x = 0; x < sectiondepth; x++ ) str1 += "\t";
1179   sprintf( buf, "%d", num1 );
1180   str1 += id;
1181   str1 += " = ";
1182   str1 += buf;
1183   str1 += ",";
1184   sprintf( buf, "%d", num2 );
1185   str1 += buf;
1186   str1 += ";";
1187   putLine( str1.c_str() );
1188 }
1189 
configPutPairString(const char * id,const char * str,bool escape_newlines)1190 void Datei::configPutPairString( const char *id, const char *str, bool escape_newlines )
1191 {
1192   std::string str1;
1193   char *buf;
1194   int i, o;
1195   int x;
1196 
1197   if ( ( id == NULL ) || ( str == NULL ) ) return;
1198   str1 = "";
1199   for ( x = 0; x < sectiondepth; x++ ) str1 += "\t";
1200 
1201   buf = (char*)_allocsafe( strlen( str ) * 2 + 1 );
1202   for ( i = 0, o = 0; i < (int)strlen( str ); i++ ) {
1203     if ( str[i] == '"' ) {
1204       buf[o++] = '\\';
1205       buf[o++] = '"';
1206     } else if ( str[i] == '\\' ) {
1207       buf[o++] = '\\';
1208       buf[o++] = '\\';
1209     } else if ( str[i] == '\n' ) {
1210         if ( escape_newlines ) {
1211             buf[o++] = '\\';
1212             buf[o++] = '\n';
1213         }
1214     } else {
1215       buf[o++] = str[i];
1216     }
1217   }
1218   buf[o] = '\0';
1219   str1 += id;
1220   str1 += " = \"";
1221   str1 += buf;
1222   str1 += "\";";
1223   _freesafe( buf );
1224   putLine( str1.c_str() );
1225 }
1226 
configPutPairBool(const char * id,bool val)1227 void Datei::configPutPairBool( const char *id, bool val )
1228 {
1229   std::string str1;
1230   int x;
1231 
1232   if ( id == NULL ) return;
1233   str1 = "";
1234   for ( x = 0; x < sectiondepth; x++ ) str1 += "\t";
1235 
1236   str1 += id;
1237   str1 += " = ";
1238   str1 += ( ( val == true ) ? "true" : "false" );
1239   str1 += ";";
1240   putLine( str1.c_str() );
1241 }
1242 
configPutInfo(const char * str,bool semicolon)1243 void Datei::configPutInfo( const char *str, bool semicolon )
1244 {
1245   std::string str1;
1246   int x;
1247 
1248   if ( str == NULL ) return;
1249   str1 = "";
1250   for ( x = 0; x < sectiondepth; x++ ) str1 += "\t";
1251 
1252   str1 += str;
1253   if ( semicolon == true ) str1 += ";";
1254   putLine( str1.c_str() );
1255 }
1256 
configPutString(const char * str,bool semicolon,bool escape_newlines)1257 void Datei::configPutString( const char *str, bool semicolon, bool escape_newlines )
1258 {
1259   std::string str1;
1260   char *buf;
1261   int i, o;
1262   int x;
1263 
1264   if ( str == NULL ) return;
1265   str1 = "";
1266   for ( x = 0; x < sectiondepth; x++ ) str1 += "\t";
1267 
1268   buf = (char*)_allocsafe( strlen( str ) * 2 + 1 );
1269   for ( i = 0, o = 0; i < (int)strlen( str ); i++ ) {
1270     if ( str[i] == '"' ) {
1271       buf[o++] = '\\';
1272       buf[o++] = '"';
1273     } else if ( str[i] == '\\' ) {
1274       buf[o++] = '\\';
1275       buf[o++] = '\\';
1276     } else if ( str[i] == '\n' ) {
1277         if ( escape_newlines ) {
1278             buf[o++] = '\\';
1279             buf[o++] = '\n';
1280         }
1281     } else {
1282       buf[o++] = str[i];
1283     }
1284   }
1285   buf[o] = '\0';
1286   str1 += "\"";
1287   str1 += buf;
1288   str1 += "\";";
1289   _freesafe( buf );
1290   putLine( str1.c_str() );
1291 }
1292 
configOpenSection(const char * sectioname)1293 void Datei::configOpenSection( const char *sectioname )
1294 {
1295   int x;
1296   std::string str1;
1297 
1298   if ( sectioname == NULL ) return;
1299   if ( ( sectiondepth < 0 ) || ( sectiondepth > 100 ) ) sectiondepth = 0;
1300 
1301   str1 = "";
1302   for ( x = 0; x < sectiondepth; x++ ) str1 += "\t";
1303 
1304   str1 += sectioname;
1305   str1 += " {";
1306   putLine( str1.c_str() );
1307   sectiondepth++;
1308 }
1309 
configCloseSection()1310 void Datei::configCloseSection()
1311 {
1312   int x;
1313   std::string str1;
1314 
1315   sectiondepth--;
1316   if ( ( sectiondepth < 0 ) || ( sectiondepth > 100 ) ) sectiondepth = 0;
1317 
1318   str1 = "";
1319   for ( x = 0; x < sectiondepth; x++ ) str1 += "\t";
1320 
1321   str1 += "}";
1322   putLine( str1.c_str() );
1323 }
1324 
read(char * buffer,size_t len)1325 ssize_t Datei::read( char *buffer, size_t len )
1326 {
1327     if ( buffer == NULL || len < 1 ) return 0;
1328     if ( fd < 0 ) return 0;
1329 
1330     ssize_t count;
1331 
1332     count = ::worker_read( fd, buffer, len );
1333 
1334     if ( count < 1 ) {
1335         iseof = true;
1336     }
1337     return count;
1338 }
1339 
~CopyfileProgressCallback()1340 CopyfileProgressCallback::~CopyfileProgressCallback()
1341 {
1342 }
1343