1 /* lowlevelfunc.cc
2 * This file belongs to Worker, a file manager for UN*X/X11.
3 * Copyright (C) 2001-2019 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 "lowlevelfunc.h"
23 #include <locale.h>
24 #include <algorithm>
25 #include "utf8.hh"
26
_allocsafe(size_t size)27 void *_allocsafe(size_t size)
28 { /* gibt Speicher zur�ck oder beendet sofort das Programm */
29 void *ptr;
30
31 ptr=(void*)malloc(size);
32 if(ptr==NULL) {
33 fprintf( stderr, "Worker Error:No Memory!!!!Aborting\n" );
34 exit(1);
35 }
36 return ptr;
37 }
38
waittime(unsigned long msec)39 void waittime(unsigned long msec)
40 {
41 #ifdef HAVE_NANOSLEEP
42 struct timespec wt;
43 wt.tv_nsec = ( msec % 1000 ) * 1000000;
44 wt.tv_sec = msec / 1000;
45 nanosleep( &wt, NULL );
46 #else
47
48 #ifdef HAVE_USLEEP
49 usleep( msec * 1000 );
50 #else
51
52 /* configure doesn't allow continue without nanosleep and usleep
53 * but worker could run without a working waittime (but not very nice)
54 * just throw a warning
55 */
56 #warning empty wait-function
57
58 #endif /* HAVE_USLEEP */
59
60 #endif /* HAVE_NANOSLEEP */
61 }
62
dupstring(const char * str)63 char *dupstring(const char *str)
64 {
65 char *tstr;
66 tstr=(char*)_allocsafe(strlen(str)+1);
67 strcpy(tstr,str);
68 return tstr;
69 }
70
MakeLong2NiceStr(loff_t size,std::string & buffer,bool do_nice)71 int MakeLong2NiceStr( loff_t size, std::string &buffer, bool do_nice )
72 {
73 static std::string own_thousands_sep;
74 struct lconv *current_locale;
75 std::string numstr;
76 loff_t tmpval = size;
77 int digit, i;
78
79 if ( own_thousands_sep.length() < 1 ) {
80 // thousands separator isn't set so try to determine it
81 current_locale = localeconv();
82 /* check if the locale defines thousands_sep */
83 if ( ( current_locale->thousands_sep != NULL ) &&
84 ( *( current_locale->thousands_sep ) != 0 ) ) {
85 /* yes, it does so store the string */
86 own_thousands_sep = current_locale->thousands_sep;
87 } else {
88 // default is the dot
89 own_thousands_sep = ".";
90 }
91
92 // reverse string because we will build the number backwards
93 std::reverse( own_thousands_sep.begin(),
94 own_thousands_sep.end() );
95 }
96
97 numstr = "";
98 i = 0;
99 do {
100 digit = abs( (int)( tmpval % 10 ) );
101 tmpval /= 10;
102
103 if ( ( do_nice == true ) && ( i != 0 ) && ( i % 3 == 0 ) )
104 numstr += own_thousands_sep;
105
106 numstr += '0' + digit;
107 i++;
108 } while ( tmpval != 0 );
109 if ( size < 0 ) numstr += '-';
110
111 std::reverse( numstr.begin(), numstr.end() );
112 buffer = numstr;
113
114 return numstr.length();
115 }
116
catstring(const char * str1,const char * str2)117 char *catstring(const char *str1,const char *str2)
118 {
119 char *tstr;
120 tstr=(char*)_allocsafe(strlen(str1)+strlen(str2)+1);
121 strcpy(tstr,str1);
122 strcpy(tstr+strlen(str1),str2);
123 return tstr;
124 }
125
shrinkstring(const char * str,int maxlen,AWidth & lencalc)126 char *shrinkstring( const char *str, int maxlen, AWidth &lencalc )
127 {
128 int width, rwidth, ddd_width, real_width, rest_width, lwidth;
129 int llen;
130 char *newstr;
131 const char *hitstr, *trystr;
132 std::string str1;
133 size_t charlen;
134 int pos;
135
136 width = lencalc.getWidth( str );
137
138 if ( width <= maxlen ) return dupstring( str );
139
140 ddd_width = lencalc.getWidth( "..." );
141
142 charlen = UTF8::getLenOfCharacter( str );
143 lwidth = lencalc.getWidth( str, charlen );
144
145 if ( ( lwidth + ddd_width ) <= maxlen) {
146 rest_width = maxlen - ddd_width;
147
148 llen = lencalc.getStrlen4Width( str, rest_width / 2, &real_width );
149 str1 = "";
150 str1.append( str, llen );
151 str1 += "...";
152
153 rest_width -= real_width;
154
155 // now find remaining character from right end
156 hitstr = NULL;
157
158 pos = strlen( str );
159 UTF8::movePosToPrevChar( str, pos );
160 trystr = str + pos;
161 while ( trystr >= str ) {
162 rwidth = lencalc.getWidth( trystr );
163 if ( rwidth <= rest_width ) hitstr = trystr;
164 else break;
165 if ( UTF8::movePosToPrevChar( str, pos ) < 1 ) break;
166 trystr = str + pos;
167 }
168
169 if ( hitstr != NULL ) {
170 str1 += hitstr;
171 }
172
173 newstr = dupstring( str1.c_str() );
174 } else {
175 llen = lencalc.getStrlen4Width( str, maxlen, &real_width );
176 newstr = dupstring( str );
177 newstr[llen] = 0;
178 }
179 return newstr;
180 }
181
diffgtod(struct timeval * tv1,struct timeval * tv0)182 double diffgtod( struct timeval *tv1, struct timeval *tv0 )
183 {
184 long s, us;
185
186 if ( ( tv1 == NULL ) || ( tv0 == NULL ) ) return 0.0;
187
188 s = tv1->tv_sec - tv0->tv_sec;
189 us = tv1->tv_usec - tv0->tv_usec;
190
191 return ((double)s) * 1000000.0 + (double)us;
192 }
193
ldiffgtod(struct timeval * tv1,struct timeval * tv0)194 long ldiffgtod( struct timeval *tv1, struct timeval *tv0 )
195 {
196 long s, us;
197
198 if ( ( tv1 == NULL ) || ( tv0 == NULL ) ) return 0;
199
200 s = tv1->tv_sec - tv0->tv_sec;
201 us = tv1->tv_usec - tv0->tv_usec;
202
203 return s * 1000000 + us;
204 }
205
ldiffgtod_m(const struct timeval * tv1,const struct timeval * tv0)206 long ldiffgtod_m( const struct timeval *tv1, const struct timeval *tv0 )
207 {
208 long s, us;
209
210 if ( ( tv1 == NULL ) || ( tv0 == NULL ) ) return 0;
211
212 s = tv1->tv_sec - tv0->tv_sec;
213 us = tv1->tv_usec - tv0->tv_usec;
214
215 return s * 1000 + us / 1000;
216 }
217
218 /* this function calculate the quote mode at the end of str
219 0 = no quotes
220 1 = single quote
221 2 = double quote
222 -1 = error
223 */
AGUIX_getQuoteMode(const char * str)224 int AGUIX_getQuoteMode( const char *str )
225 {
226 int quotemode = 0; /* 0 means no quote
227 1 means in single quotes
228 2 means in double quotes */
229 int i, len;
230
231 if ( str == NULL ) return -1;
232
233 len = strlen( str );
234
235 for ( i = 0; i < len; i++ ) {
236 switch ( quotemode ) {
237 case 1:
238 // even backslash doesn't matter in single-quotes so just check for closing quote
239 if ( str[i] == '\'' ) {
240 // you cannot do single quote inside double quote so we return to "no quote"
241 quotemode = 0;
242 }
243 break;
244 case 2:
245 // this is more complicated
246 // the backslash doesn't protect everything just special characters
247 // anyway if the following char isn't special we don't care on it
248 // and if it's a special we also don't care because it's backslashed
249 // so it's safe to skip next char in any case
250 if ( str[i] == '\\' ) {
251 i++;
252 } else if ( str[i] == '"' ) {
253 // remember that we skiped this char if it was backslashed
254 // so we really have to leave quote mode and for the same reason
255 // as above it's not possibly to do double quote in single quote
256 // we can go to "no quotes" mode
257 quotemode = 0;
258 }
259 break;
260 default:
261 if ( str[i] == '\\' ) {
262 // not in quotes => ignore next
263 i++;
264 } else if ( str[i] == '"' ) {
265 // double quote begin
266 quotemode = 2;
267 } else if ( str[i] == '\'' ) {
268 // single quote begin
269 quotemode = 1;
270 }
271 break;
272 }
273 }
274 return quotemode;
275 }
276
277 /* this function will cat 2 strings:
278 str1 have to be a string which STARTS unquoted (can begin with quotes
279 but it's interpreted as beginning quotes not ending for a another unknown
280 string!
281 str2 is an untrusted string and will be single quoted in any case
282 depending on quote-status at the end of str1, str2 is correctly
283 quoted in single-quotes so it can be used in shell-script to avoid problems
284 for filenames like test`rm -rf /`
285
286 str1 is not changed but between str1 and str2 and at the end
287 I add the correct quotes
288 str2 can be changed (single quotes become '\'' (to avoid any known and unknown shell actions) )
289
290 return string will always end in same quotes like str1
291 */
AGUIX_catTrustedAndUnTrusted(const char * str1,const char * str2)292 char *AGUIX_catTrustedAndUnTrusted( const char *str1, const char *str2 )
293 {
294 int quotemode = 0; /* 0 means no quote
295 1 means in single quotes
296 2 means in double quotes */
297 int i, o, lenstr1, lenstr2, sqcount;
298 char *newstr2, *newstr, *tstr1;
299
300 if ( ( str1 == NULL ) || ( str2 == NULL ) ) return NULL;
301
302 // first fix backslashed str1 because it a last backslash
303 // will protect the quoting for str2
304 tstr1 = AGUIX_fixBackslashed( str1 );
305 if ( tstr1 == NULL ) return NULL;
306
307 lenstr1 = strlen( tstr1 );
308
309 quotemode = AGUIX_getQuoteMode( tstr1 );
310 if ( quotemode < 0 ) return NULL;
311
312 // now we know in which quote mode str1 is at the end
313 // se we can do the following:
314 // in single quotes we have just to replace singlequotes with '\''
315 // in double quotes we have to close double close and add single quoted filename
316 // in no quotes we just have to add single quoted filename
317
318 // since filename will always be inside single quotes first replace all single quotes
319 // in filename with '\''
320 lenstr2 = strlen( str2 );
321 sqcount = 0;
322 for ( i = 0; i < lenstr2; i++ ) {
323 if ( str2[i] == '\'' ) sqcount++;
324 }
325 // for every single quote we need 3 additional chars
326 newstr2 = (char*)_allocsafe( lenstr2 + sqcount * 3 +
327 1 /* null byte */ +
328 3 /* additional dummy bytes for optimized strlen */ );
329 for ( i = 0, o = 0; i < lenstr2; i++ ) {
330 if ( str2[i] == '\'' ) {
331 newstr2[o++] = '\'';
332 newstr2[o++] = '\\';
333 newstr2[o++] = '\'';
334 newstr2[o++] = '\'';
335 } else {
336 newstr2[o++] = str2[i];
337 }
338 }
339 newstr2[o] = '\0';
340
341 /* just to keep optimized strlen and co quiet */
342 newstr2[o+1] = '\0';
343 newstr2[o+2] = '\0';
344 newstr2[o+3] = '\0';
345
346 // newfilename is now okay
347 // space needed for newstr1:
348 // str1len
349 // perhaps a closing double quote and a beginning single quote
350 // strlen( newfilename )
351 // perhaps a closing single quote and beginning double quote
352 newstr = (char*)_allocsafe( lenstr1 + 2 + strlen( newstr2 ) + 2 + 1 );
353 switch ( quotemode ) {
354 case 1:
355 // single quote so no need for quote changing
356 // remember newfilename is correctly quoted to live inside
357 // single quote
358 sprintf( newstr, "%s%s", tstr1, newstr2 );
359 break;
360 case 2:
361 // double quotes so close double quotes, begin single quote
362 // and after newfilename close single quote and start double quote
363 sprintf( newstr, "%s\"'%s'\"", tstr1, newstr2 );
364 break;
365 default:
366 // no quote so just start single quote and after newfilename single quote
367 sprintf( newstr, "%s'%s'", tstr1, newstr2 );
368 break;
369 }
370 _freesafe( newstr2 );
371 _freesafe( tstr1 );
372 return newstr;
373 }
374
375 /* this function replace all single quotes with '\'' so it can be
376 used in single quotes */
AGUIX_prepareForSingleQuote(const char * str1)377 char *AGUIX_prepareForSingleQuote( const char *str1 )
378 {
379 int i, o, lenstr1, sqcount;
380 char *newstr1;
381
382 if ( str1 == NULL ) return NULL;
383
384 // single quotes in filename with '\''
385 lenstr1 = strlen( str1 );
386 sqcount = 0;
387 for ( i = 0; i < lenstr1; i++ ) {
388 if ( str1[i] == '\'' ) sqcount++;
389 }
390 // for every single quote we need 3 additional chars
391 newstr1 = (char*)_allocsafe( lenstr1 + sqcount * 3 + 1 );
392 for ( i = 0, o = 0; i < lenstr1; i++ ) {
393 if ( str1[i] == '\'' ) {
394 newstr1[o++] = '\'';
395 newstr1[o++] = '\\';
396 newstr1[o++] = '\'';
397 newstr1[o++] = '\'';
398 } else {
399 newstr1[o++] = str1[i];
400 }
401 }
402 newstr1[o] = '\0';
403 return newstr1;
404 }
405
406 /* this function will remove all quotes from the string
407 rules:
408 single quotes: allowed, no special character
409 double quotes: allowed, backslash escapes next character
410 single and double quotes can be mixed
411 */
AGUIX_unquoteString(const char * str1)412 char *AGUIX_unquoteString( const char *str1 )
413 {
414 char *newstr;
415 int quotemode = 0; /* 0 means no quote
416 1 means in single quotes
417 2 means in double quotes */
418 int i, o, len;
419
420 if ( str1 == NULL ) return NULL;
421 newstr = (char*)_allocsafe( strlen( str1 ) + 1 );
422
423 len = strlen( str1 );
424
425 for ( i = 0, o = 0; i < len; i++ ) {
426 switch ( quotemode ) {
427 case 1:
428 // even backslash doesn't matter in single-quotes so just check for closing quote
429 if ( str1[i] == '\'' ) {
430 // you cannot do single quote inside double quote so we return to "no quote"
431 quotemode = 0;
432 } else {
433 newstr[o++] = str1[i];
434 }
435 break;
436 case 2:
437 // this is more complicated
438 // the backslash doesn't protect everything just special characters
439 // anyway if the following char isn't special we don't care on it
440 // and if it's a special we also don't care because it's backslashed
441 // so it's safe to skip next char in any case
442 if ( str1[i] == '\\' ) {
443 // only backslash $, `, ", <backslash
444 // (just like shell (at least bash))
445 switch( str1[i + 1] ) {
446 case '$':
447 case '`':
448 case '"':
449 case '\\':
450 i++;
451 default:
452 newstr[o++] = str1[i];
453 break;
454 }
455 } else if ( str1[i] == '"' ) {
456 // remember that we skiped this char if it was backslashed
457 // so we really have to leave quote mode and for the same reason
458 // as above it's not possibly to do double quote in single quote
459 // we can go to "no quotes" mode
460 quotemode = 0;
461 } else {
462 newstr[o++] = str1[i];
463 }
464 break;
465 default:
466 if ( str1[i] == '\\' ) {
467 // backslash in nonquoted env escapes everything
468 i++;
469 newstr[o++] = str1[i];
470 } else if ( str1[i] == '"' ) {
471 // double quote begin
472 quotemode = 2;
473 } else if ( str1[i] == '\'' ) {
474 // single quote begin
475 quotemode = 1;
476 } else {
477 newstr[o++] = str1[i];
478 }
479 break;
480 }
481 }
482 newstr[o] = '\0';
483
484 return newstr;
485 }
486
487 /*
488 * this function will cat 2 strings
489 * the first one can have single and double quotes
490 * the second will be protected according to the quote-mode at the end
491 * of str1
492 *
493 * what's done here is not enough for direct shell output because special
494 * shell stuff like $... is not protected
495 * use catTrustedAndUnTrusted instead
496 *
497 * this function is enough for secureCommand... from execlass
498 *
499 * following rules apply:
500 * 1.str1 not quoted at the end: whitespaces, single,double quotes and backslash
501 * are protected with backslash
502 * 2.str1 ends with single quote: single quotes are replaced with '\''
503 * 3.str1 ends with double quote: double quotes and backslash are protected with backslash
504 */
AGUIX_catQuotedAndUnQuoted(const char * str1,const char * str2)505 char *AGUIX_catQuotedAndUnQuoted( const char *str1, const char *str2 )
506 {
507 int quotemode = 0; /* 0 means no quote
508 1 means in single quotes
509 2 means in double quotes */
510 int i, o, lenstr1, lenstr2, newlen;
511 char *newstr, *tstr1;
512
513 if ( ( str1 == NULL ) || ( str2 == NULL ) ) return NULL;
514
515 // first fix backslashed str1
516 tstr1 = AGUIX_fixBackslashed( str1 );
517 if ( tstr1 == NULL ) return NULL;
518
519 lenstr1 = strlen( tstr1 );
520
521 quotemode = AGUIX_getQuoteMode( tstr1 );
522 if ( quotemode < 0 ) return NULL;
523
524 /* to know the length of the new string
525 * count all whitespaces, single and double quotes in it
526 * and add the needed space to protect them
527 */
528 lenstr2 = strlen( str2 );
529 newlen = 0;
530 for ( i = 0; i < lenstr2; i++, newlen++ ) {
531 if ( str2[i] == '\'' ) {
532 newlen += 3; /* in quotemode 1 I have to protect it
533 * with '\'' so I need 3 additional chars */
534 } else if ( str2[i] == '\"' ) {
535 newlen++;
536 } else if ( str2[i] == '\\' ) {
537 newlen++;
538 } else if ( str2[i] == '$' ) {
539 newlen++;
540 } else if ( str2[i] == '&' ) {
541 newlen++;
542 } else if ( str2[i] == '|' ) {
543 newlen++;
544 } else if ( str2[i] == ';' ) {
545 newlen++;
546 } else if ( str2[i] == '>' ) {
547 newlen++;
548 } else if ( str2[i] == '<' ) {
549 newlen++;
550 } else if ( isspace( str2[i] ) ) {
551 newlen++;
552 }
553 }
554
555 newstr = (char*)_allocsafe( lenstr1 + newlen + 1 );
556 strcpy( newstr, tstr1 );
557 o = lenstr1;
558
559 for ( i = 0; i < lenstr2; i++ ) {
560 switch ( quotemode ) {
561 case 1:
562 if ( str2[i] == '\'' ) {
563 newstr[o++] = '\'';
564 newstr[o++] = '\\';
565 newstr[o++] = '\'';
566 newstr[o++] = '\'';
567 } else {
568 newstr[o++] = str2[i];
569 }
570 break;
571 case 2:
572 if ( str2[i] == '"' ) {
573 newstr[o++] = '\\';
574 newstr[o++] = '"';
575 } else if ( str2[i] == '\\' ) {
576 newstr[o++] = '\\';
577 newstr[o++] = '\\';
578 } else if ( str2[i] == '$' ) {
579 newstr[o++] = '\\';
580 newstr[o++] = '$';
581 } else {
582 newstr[o++] = str2[i];
583 }
584 break;
585 default:
586 if ( str2[i] == '"' ) {
587 newstr[o++] = '\\';
588 newstr[o++] = '"';
589 } else if ( str2[i] == '\'' ) {
590 newstr[o++] = '\\';
591 newstr[o++] = '\'';
592 } else if ( str2[i] == '\\' ) {
593 newstr[o++] = '\\';
594 newstr[o++] = '\\';
595 } else if ( str2[i] == '$' ) {
596 newstr[o++] = '\\';
597 newstr[o++] = '$';
598 } else if ( str2[i] == '&' ) {
599 newstr[o++] = '\\';
600 newstr[o++] = '&';
601 } else if ( str2[i] == '|' ) {
602 newstr[o++] = '\\';
603 newstr[o++] = '|';
604 } else if ( str2[i] == ';' ) {
605 newstr[o++] = '\\';
606 newstr[o++] = ';';
607 } else if ( str2[i] == '<' ) {
608 newstr[o++] = '\\';
609 newstr[o++] = '<';
610 } else if ( str2[i] == '>' ) {
611 newstr[o++] = '\\';
612 newstr[o++] = '>';
613 } else if ( isspace( str2[i] ) ) {
614 newstr[o++] = '\\';
615 newstr[o++] = str2[i];
616 } else {
617 newstr[o++] = str2[i];
618 }
619 break;
620 }
621 }
622 newstr[o] = '\0';
623
624 _freesafe( tstr1 );
625
626 return newstr;
627 }
628
629 /*
630 * this function will remove a backslash at the end
631 * if this backslash
632 * 1.exists
633 * 2.is not inside single quotes (backslash doesn't matter there)
634 * 3.the backslash is not backslashed (\\)
635 */
AGUIX_fixBackslashed(const char * str1)636 char *AGUIX_fixBackslashed( const char *str1 )
637 {
638 char *newstr;
639 int lenstr1, bscount, i;
640 int quotemode;
641
642 if ( str1 == NULL ) return NULL;
643
644 lenstr1 = strlen( str1 );
645 if ( lenstr1 < 1 ) return dupstring( str1 );
646
647 bscount = 0;
648 for ( i = lenstr1 - 1; i >= 0; i-- ) {
649 if ( str1[ i ] == '\\' ) {
650 bscount++;
651 } else {
652 break;
653 }
654 }
655
656 // even backslash count is okay because two
657 // protects a backslash
658
659 quotemode = AGUIX_getQuoteMode( str1 );
660 if ( quotemode < 0 ) return NULL;
661
662 newstr = dupstring( str1 );
663 if ( quotemode != 1 ) {
664 // backslash is no special character in single quotes
665 if ( ( bscount % 2 ) == 1 ) {
666 // clear last backslash
667 newstr[ lenstr1 - 1 ] = '\0';
668 }
669 }
670 return newstr;
671 }
672
AGUIX_catTrustedAndUnTrusted2(const std::string & str1,const std::string & str2)673 std::string AGUIX_catTrustedAndUnTrusted2( const std::string &str1,
674 const std::string &str2 )
675 {
676 char *tstr = AGUIX_catTrustedAndUnTrusted( str1.c_str(), str2.c_str() );
677
678 if ( ! tstr ) return "";
679 std::string res = tstr;
680 _freesafe( tstr );
681
682 return res;
683 }
684
AGUIX_catQuotedAndUnQuoted2(const std::string & str1,const std::string & str2)685 std::string AGUIX_catQuotedAndUnQuoted2( const std::string &str1,
686 const std::string &str2 )
687 {
688 char *tstr = AGUIX_catQuotedAndUnQuoted( str1.c_str(), str2.c_str() );
689
690 if ( ! tstr ) return "";
691 std::string res = tstr;
692 _freesafe( tstr );
693
694 return res;
695 }
696
AGUIX_prepareForSingleQuote2(const std::string str)697 std::string AGUIX_prepareForSingleQuote2( const std::string str )
698 {
699 char *tstr = AGUIX_prepareForSingleQuote( str.c_str() );
700
701 if ( ! tstr ) return "";
702 std::string res = tstr;
703 _freesafe( tstr );
704
705 return res;
706 }
707