1 /* stringfile.c: manage strings, files, and directories for edbrowse.
2 * This file is part of the edbrowse project, released under GPL.
3 */
4
5 #include "eb.h"
6
7 #include <dirent.h>
8 #ifdef DOSLIKE
9 #include <dos.h>
10 #else
11 #include <termios.h>
12 #include <unistd.h>
13 #include <glob.h>
14 #include <pwd.h>
15 #include <grp.h>
16 #endif
17
18 char emptyString[] = "";
19 bool showHiddenFiles, isInteractive;
20 int debugLevel = 1;
21 bool debugClone, debugEvent, debugThrow, debugCSS;
22 FILE *debugFile = NULL;
23 char *debugFileName;
24 bool demin = false;
25 bool gotimers = true;
26 bool uvw;
27 char *downDir, *home;
28
29 /*********************************************************************
30 Allocate and manage memory.
31 Allocate and copy strings.
32 If we're out of memory, the program aborts. No error legs.
33 Soooooo much easier! With 32gb of RAM, we shouldn't run out.
34 *********************************************************************/
35
allocMem(size_t n)36 void *allocMem(size_t n)
37 {
38 void *s;
39 if (!n)
40 return emptyString;
41 if (!(s = malloc(n)))
42 i_printfExit(MSG_MemAllocError, n);
43 return s;
44 } /* allocMem */
45
allocZeroMem(size_t n)46 void *allocZeroMem(size_t n)
47 {
48 void *s;
49 if (!n)
50 return emptyString;
51 if (!(s = calloc(n, 1)))
52 i_printfExit(MSG_MemCallocError, n);
53 return s;
54 } /* allocZeroMem */
55
reallocMem(void * p,size_t n)56 void *reallocMem(void *p, size_t n)
57 {
58 void *s;
59 if (!n)
60 i_printfExit(MSG_ReallocP);
61 if (!p)
62 i_printfExit(MSG_Realloc0, n);
63 if (p == emptyString) {
64 p = allocMem(n);
65 // keep the null byte that was present in emptyString.
66 // fileIntoMemory() needs this to keep null on the end of an empty file
67 // that was just read into memory.
68 *(char *)p = 0;
69 }
70 if (!(s = realloc(p, n)))
71 i_printfExit(MSG_ErrorRealloc, n);
72 return s;
73 } /* reallocMem */
74
75 /* When you know the allocated thing is a string. */
allocString(size_t n)76 char *allocString(size_t n)
77 {
78 return (char *)allocMem(n);
79 } /* allocString */
80
allocZeroString(size_t n)81 char *allocZeroString(size_t n)
82 {
83 return (char *)allocZeroMem(n);
84 } /* allocZeroString */
85
reallocString(void * p,size_t n)86 char *reallocString(void *p, size_t n)
87 {
88 return (char *)reallocMem(p, n);
89 } /* reallocString */
90
nzFree(void * s)91 void nzFree(void *s)
92 {
93 if (s && s != emptyString)
94 free(s);
95 } /* nzFree */
96
97 /* some compilers care whether it's void * or const void * */
cnzFree(const void * v)98 void cnzFree(const void *v)
99 {
100 nzFree((void *)v);
101 } /* cnzFree */
102
appendString(char * s,const char * p)103 char *appendString(char *s, const char *p)
104 {
105 int slen = strlen(s);
106 int plen = strlen(p);
107 s = reallocString(s, slen + plen + 1);
108 strcpy(s + slen, p);
109 return s;
110 } /* appendstring */
111
prependString(char * s,const char * p)112 char *prependString(char *s, const char *p)
113 {
114 int slen = strlen(s);
115 int plen = strlen(p);
116 char *t = allocString(slen + plen + 1);
117 strcpy(t, p);
118 strcpy(t + plen, s);
119 nzFree(s);
120 return t;
121 } /* prependstring */
122
skipWhite(const char ** s)123 void skipWhite(const char **s)
124 {
125 const char *t = *s;
126 while (isspaceByte(*t))
127 ++t;
128 *s = t;
129 } /* skipWhite */
130
trimWhite(char * s)131 void trimWhite(char *s)
132 {
133 int l;
134 if (!s)
135 return;
136 l = strlen(s);
137 while (l && isspaceByte(s[l - 1]))
138 --l;
139 s[l] = 0;
140 } /* trimWhite */
141
stripWhite(char * s)142 void stripWhite(char *s)
143 {
144 const char *t = s;
145 skipWhite(&t);
146 if (t > s)
147 strmove(s, t);
148 trimWhite(s);
149 } /* stripWhite */
150
151 /* compress white space */
spaceCrunch(char * s,bool onespace,bool unprint)152 void spaceCrunch(char *s, bool onespace, bool unprint)
153 {
154 int i, j;
155 char c;
156 bool space = true;
157 for (i = j = 0; (c = s[i]); ++i) {
158 if (isspaceByte(c)) {
159 if (!onespace)
160 continue;
161 if (!space)
162 s[j++] = ' ', space = true;
163 continue;
164 }
165 if (unprint && !isprintByte(c))
166 continue;
167 s[j++] = c, space = false;
168 }
169 if (space && j)
170 --j; /* drop trailing space */
171 s[j] = 0;
172 } /* spaceCrunch */
173
174 /* Like strcpy, but able to cope with overlapping strings. */
strmove(char * dest,const char * src)175 char *strmove(char *dest, const char *src)
176 {
177 return (char *)memmove(dest, src, strlen(src) + 1);
178 } /* strmove */
179
180 /* OO has a lot of unnecessary overhead, and a few inconveniences,
181 * but I really miss it right now. The following
182 * routines make up for the lack of simple string concatenation in C.
183 * The string space allocated is always a power of 2 - 1, starting with 1.
184 * Each of these routines puts an extra 0 on the end of the "string". */
185
initString(int * l)186 char *initString(int *l)
187 {
188 *l = 0;
189 return emptyString;
190 }
191
192 /* String management routines realloc to one less than a power of 2 */
stringAndString(char ** s,int * l,const char * t)193 void stringAndString(char **s, int *l, const char *t)
194 {
195 char *p = *s;
196 int oldlen, newlen, x;
197 oldlen = *l;
198 newlen = oldlen + strlen(t);
199 *l = newlen;
200 ++newlen; /* room for the 0 */
201 x = oldlen ^ newlen;
202 if (x > oldlen) { /* must realloc */
203 newlen |= (newlen >> 1);
204 newlen |= (newlen >> 2);
205 newlen |= (newlen >> 4);
206 newlen |= (newlen >> 8);
207 newlen |= (newlen >> 16);
208 p = reallocString(p, newlen);
209 *s = p;
210 }
211 strcpy(p + oldlen, t);
212 } /* stringAndString */
213
stringAndBytes(char ** s,int * l,const char * t,int cnt)214 void stringAndBytes(char **s, int *l, const char *t, int cnt)
215 {
216 char *p = *s;
217 int oldlen, newlen, x;
218 oldlen = *l;
219 newlen = oldlen + cnt;
220 *l = newlen;
221 ++newlen;
222 x = oldlen ^ newlen;
223 if (x > oldlen) { /* must realloc */
224 newlen |= (newlen >> 1);
225 newlen |= (newlen >> 2);
226 newlen |= (newlen >> 4);
227 newlen |= (newlen >> 8);
228 newlen |= (newlen >> 16);
229 p = reallocString(p, newlen);
230 *s = p;
231 }
232 memcpy(p + oldlen, t, cnt);
233 p[oldlen + cnt] = 0;
234 } /* stringAndBytes */
235
stringAndChar(char ** s,int * l,char c)236 void stringAndChar(char **s, int *l, char c)
237 {
238 char *p = *s;
239 int oldlen, newlen, x;
240 oldlen = *l;
241 newlen = oldlen + 1;
242 *l = newlen;
243 ++newlen;
244 x = oldlen ^ newlen;
245 if (x > oldlen) { /* must realloc */
246 newlen |= (newlen >> 1);
247 newlen |= (newlen >> 2);
248 newlen |= (newlen >> 4);
249 newlen |= (newlen >> 8);
250 newlen |= (newlen >> 16);
251 p = reallocString(p, newlen);
252 *s = p;
253 }
254 p[oldlen] = c;
255 p[oldlen + 1] = 0;
256 } /* stringAndChar */
257
stringAndNum(char ** s,int * l,int n)258 void stringAndNum(char **s, int *l, int n)
259 {
260 char a[16];
261 sprintf(a, "%d", n);
262 stringAndString(s, l, a);
263 } /* stringAndNum */
264
265 /* 64M 16K etc */
stringAndKnum(char ** s,int * l,int n)266 void stringAndKnum(char **s, int *l, int n)
267 {
268 char a[16];
269 if (n && n / (1024 * 1024) * (1024 * 1024) == n)
270 sprintf(a, "%dM", n / (1024 * 1024));
271 else if (n && n / 1024 * 1024 == n)
272 sprintf(a, "%dK", n / 1024);
273 else
274 sprintf(a, "%d", n);
275 stringAndString(s, l, a);
276 } /* stringAndKnum */
277
cloneString(const char * s)278 char *cloneString(const char *s)
279 {
280 char *t;
281 unsigned len;
282
283 if (!s)
284 return 0;
285 if (!*s)
286 return emptyString;
287 len = strlen(s) + 1;
288 t = allocString(len);
289 strcpy(t, s);
290 return t;
291 } /* cloneString */
292
cloneMemory(const char * s,int n)293 char *cloneMemory(const char *s, int n)
294 {
295 char *t = allocString(n);
296 if (n)
297 memcpy(t, s, n);
298 return t;
299 } /* cloneMemory */
300
leftClipString(char * s)301 void leftClipString(char *s)
302 {
303 char *t;
304 if (!s)
305 return;
306 for (t = s; *t; ++t)
307 if (!isspace(*t))
308 break;
309 if (t > s)
310 strmove(s, t);
311 } /* leftClipString */
312
313 /* shift string one to the right */
shiftRight(char * s,char first)314 void shiftRight(char *s, char first)
315 {
316 int l = strlen(s);
317 for (; l >= 0; --l)
318 s[l + 1] = s[l];
319 s[0] = first;
320 } /* shiftRight */
321
Cify(const char * s,int n)322 char *Cify(const char *s, int n)
323 {
324 char *u;
325 char *t = allocString(n + 1);
326 if (n)
327 memcpy(t, s, n);
328 for (u = t; u < t + n; ++u)
329 if (*u == 0)
330 *u = ' ';
331 *u = 0;
332 return t;
333 } /* Cify */
334
335 /* pull a substring out of a larger string,
336 * and make it its own allocated string */
pullString(const char * s,int l)337 char *pullString(const char *s, int l)
338 {
339 char *t;
340 if (!l)
341 return emptyString;
342 t = allocString(l + 1);
343 memcpy(t, s, l);
344 t[l] = 0;
345 return t;
346 } /* pullString */
347
pullString1(const char * s,const char * t)348 char *pullString1(const char *s, const char *t)
349 {
350 return pullString(s, t - s);
351 }
352
353 /* return the number, if string is a number, else -1 */
stringIsNum(const char * s)354 int stringIsNum(const char *s)
355 {
356 int n;
357 if (!isdigitByte(s[0]))
358 return -1;
359 n = strtol(s, (char **)&s, 10);
360 if (*s)
361 return -1;
362 return n;
363 } /* stringIsNum */
364
stringIsDate(const char * s)365 bool stringIsDate(const char *s)
366 {
367 if (!isdigit(*s))
368 return false;
369 ++s;
370 if (isdigit(*s))
371 ++s;
372 if (*s++ != '/')
373 return false;
374 if (!isdigit(*s))
375 return false;
376 ++s;
377 if (isdigit(*s))
378 ++s;
379 if (*s++ != '/')
380 return false;
381 if (!isdigit(*s))
382 return false;
383 ++s;
384 if (isdigit(*s))
385 ++s;
386 if (isdigit(*s))
387 ++s;
388 if (isdigit(*s))
389 ++s;
390 if (*s)
391 return false;
392 return true;
393 } /* stringIsDate */
394
stringIsFloat(const char * s,double * dp)395 bool stringIsFloat(const char *s, double *dp)
396 {
397 const char *t;
398 *dp = strtod(s, (char **)&t);
399 if (*t)
400 return false; /* extra stuff at the end */
401 return true;
402 } /* stringIsFloat */
403
memEqualCI(const char * s,const char * t,int len)404 bool memEqualCI(const char *s, const char *t, int len)
405 {
406 char c, d;
407 if (s == t)
408 return true;
409 if (!s || !t)
410 return false;
411 while (len--) {
412 c = *s, d = *t;
413 if (islowerByte(c))
414 c = toupper(c);
415 if (islowerByte(d))
416 d = toupper(d);
417 if (c != d)
418 return false;
419 ++s, ++t;
420 }
421 return true;
422 } /* memEqualCI */
423
strstrCI(const char * base,const char * search)424 char *strstrCI(const char *base, const char *search)
425 {
426 int l = strlen(search);
427 while (*base) {
428 if (memEqualCI(base, search, l))
429 return (char *)base;
430 ++base;
431 }
432 return 0;
433 } /* strstrCI */
434
stringEqual(const char * s,const char * t)435 bool stringEqual(const char *s, const char *t)
436 {
437 /* check equality of strings with handling of null pointers */
438 if (s == t)
439 return true;
440 if (!s || !t)
441 return false;
442 if (strcmp(s, t))
443 return false;
444 return true;
445 } /* stringEqual */
446
stringEqualCI(const char * s,const char * t)447 bool stringEqualCI(const char *s, const char *t)
448 {
449 char c, d;
450 /* if two pointers are equal we can return */
451 if (s == t)
452 return true;
453 /* if one is NULL then the strings can't be equal */
454 if (!s || !t)
455 return false;
456 while ((c = *s) && (d = *t)) {
457 if (islowerByte(c))
458 c = toupper(c);
459 if (islowerByte(d))
460 d = toupper(d);
461 if (c != d)
462 return false;
463 ++s, ++t;
464 }
465 if (*s)
466 return false;
467 if (*t)
468 return false;
469 return true;
470 } /* stringEqualCI */
471
stringInList(const char * const * list,const char * s)472 int stringInList(const char *const *list, const char *s)
473 {
474 int i = 0;
475 if (!list)
476 i_printfExit(MSG_NullStrList);
477 if (s)
478 while (*list) {
479 if (stringEqual(s, *list))
480 return i;
481 ++i;
482 ++list;
483 }
484 return -1;
485 } /* stringInList */
486
stringInListCI(const char * const * list,const char * s)487 int stringInListCI(const char *const *list, const char *s)
488 {
489 int i = 0;
490 if (!list)
491 i_printfExit(MSG_NullStrListCI);
492 if (s)
493 while (*list) {
494 if (stringEqualCI(s, *list))
495 return i;
496 ++i;
497 ++list;
498 }
499 return -1;
500 } /* stringInListCI */
501
charInList(const char * list,char c)502 int charInList(const char *list, char c)
503 {
504 char *s;
505 if (!list)
506 i_printfExit(MSG_NullCharInList);
507 s = (char *)strchr(list, c);
508 if (!s)
509 return -1;
510 return s - list;
511 } /* charInList */
512
513 /* In an empty list, next and prev point back to the list, not to 0. */
514 /* We also allow zero. */
listIsEmpty(const struct listHead * l)515 bool listIsEmpty(const struct listHead * l)
516 {
517 return l->next == l || l->next == 0;
518 } /* listIsEmpty */
519
initList(struct listHead * l)520 void initList(struct listHead *l)
521 {
522 l->prev = l->next = l;
523 } /* initList */
524
delFromList(void * x)525 void delFromList(void *x)
526 {
527 struct listHead *xh = (struct listHead *)x;
528 ((struct listHead *)xh->next)->prev = xh->prev;
529 ((struct listHead *)xh->prev)->next = xh->next;
530 } /* delFromList */
531
addToListFront(struct listHead * l,void * x)532 void addToListFront(struct listHead *l, void *x)
533 {
534 struct listHead *xh = (struct listHead *)x;
535 xh->next = l->next;
536 xh->prev = l;
537 l->next = (struct listHead *)x;
538 ((struct listHead *)xh->next)->prev = (struct listHead *)x;
539 } /* addToListFront */
540
addToListBack(struct listHead * l,void * x)541 void addToListBack(struct listHead *l, void *x)
542 {
543 struct listHead *xh = (struct listHead *)x;
544 xh->prev = l->prev;
545 xh->next = l;
546 l->prev = (struct listHead *)x;
547 ((struct listHead *)xh->prev)->next = (struct listHead *)x;
548 } /* addToListBack */
549
addAtPosition(void * p,void * x)550 void addAtPosition(void *p, void *x)
551 {
552 struct listHead *xh = (struct listHead *)x;
553 struct listHead *ph = (struct listHead *)p;
554 xh->prev = p;
555 xh->next = ph->next;
556 ph->next = (struct listHead *)x;
557 ((struct listHead *)xh->next)->prev = (struct listHead *)x;
558 } /* addAtPosition */
559
freeList(struct listHead * l)560 void freeList(struct listHead *l)
561 {
562 while (!listIsEmpty(l)) {
563 void *p = l->next;
564 delFromList(p);
565 nzFree(p);
566 }
567 } /* freeList */
568
569 /* like isalnumByte, but allows _ and - */
isA(char c)570 bool isA(char c)
571 {
572 if (isalnumByte(c))
573 return true;
574 return (c == '_' || c == '-');
575 } /* isA */
576
isquote(char c)577 bool isquote(char c)
578 {
579 return c == '"' || c == '\'';
580 } /* isquote */
581
582 /* print an error message */
errorPrint(const char * msg,...)583 void errorPrint(const char *msg, ...)
584 {
585 char bailflag = 0;
586 va_list p;
587
588 va_start(p, msg);
589
590 if (*msg == '@') {
591 bailflag = 1;
592 ++msg;
593 /* I should internationalize this, but it's never suppose to happen! */
594 fprintf(stderr, "disaster, ");
595 } else if (isdigitByte(*msg)) {
596 bailflag = *msg - '0';
597 ++msg;
598 }
599
600 vfprintf(stderr, msg, p);
601 fprintf(stderr, "\n");
602 va_end(p);
603
604 if (bailflag)
605 exit(bailflag);
606 } /* errorPrint */
607
debugPrint(int lev,const char * msg,...)608 void debugPrint(int lev, const char *msg, ...)
609 {
610 va_list p;
611 if (lev > debugLevel)
612 return;
613 if (!debugFile || lev <= 2) {
614 va_start(p, msg);
615 vprintf(msg, p);
616 va_end(p);
617 printf("\n");
618 }
619 if (debugFile) {
620 if (fseek(debugFile, 0, SEEK_END) >= 10000000) {
621 puts("debug file overflow, program aborted");
622 fclose(debugFile);
623 ebClose(3);
624 }
625 va_start(p, msg);
626 vfprintf(debugFile, msg, p);
627 va_end(p);
628 fprintf(debugFile, "\n");
629 }
630 if (lev == 0 && !memcmp(msg, "warning", 7))
631 eeCheck();
632 } /* debugPrint */
633
setDebugFile(const char * name)634 void setDebugFile(const char *name)
635 {
636 if (debugFile)
637 fclose(debugFile);
638 debugFile = 0;
639 nzFree(debugFileName);
640 debugFileName = 0;
641 if (!name || !*name)
642 return;
643 debugFileName = cloneString(name);
644 debugFile = fopen(name, (whichproc == 'e' ? "w" : "a"));
645 if (debugFile) {
646 #ifndef _MSC_VER // port setlinebuf(debugFile);, if required...
647 setlinebuf(debugFile);
648 #else
649 ;
650 #endif // !_MSC_VER
651 } else if (whichproc == 'e')
652 printf("cannot create %s\n", name);
653 } /* setDebugFile */
654
nl(void)655 void nl(void)
656 {
657 eb_puts("");
658 } /* nl */
659
660 /* Turn perl string into C string, and complain about nulls. */
perl2c(char * t)661 int perl2c(char *t)
662 {
663 int n = 0;
664 while (*t != '\n') {
665 if (*t == 0)
666 ++n;
667 ++t;
668 }
669 *t = 0; /* now it's a C string */
670 return n; /* number of nulls */
671 } /* perl2c */
672
673 /* The length of a perl string includes its terminating newline */
pstLength(pst s)674 unsigned pstLength(pst s)
675 {
676 pst t;
677 if (!s)
678 i_printfExit(MSG_NullPtr);
679 t = s;
680 while (*t != '\n')
681 ++t;
682 return t + 1 - s;
683 } /* pstLength */
684
clonePstring(pst s)685 pst clonePstring(pst s)
686 {
687 pst t;
688 unsigned len;
689 if (!s)
690 return s;
691 len = pstLength(s);
692 t = (pst) allocMem(len);
693 memcpy(t, s, len);
694 return t;
695 } /* clonePstring */
696
697 // Strings are assumed distinct, hence the use of memcpy.
copyPstring(pst s,const pst t)698 void copyPstring(pst s, const pst t)
699 {
700 int len = pstLength(t);
701 memcpy(s, t, len);
702 } /* copyPstring */
703
704 /*
705 * fdIntoMemory reads data from a file descriptor, until EOF is reached.
706 * It works even if we don't know the size beforehand.
707 * We can now use it to read /proc files, pipes, and stdin.
708 * This solves an outstanding issue, and it is needed for forthcoming
709 * functionality, such as edpager.
710 */
fdIntoMemory(int fd,char ** data,int * len)711 bool fdIntoMemory(int fd, char **data, int *len)
712 {
713 int length, n;
714 const int blocksize = 8192;
715 char *chunk, *buf;
716
717 chunk = allocZeroString(blocksize);
718 buf = initString(&length);
719
720 n = 0;
721 do {
722 n = read(fd, chunk, blocksize);
723 if (n < 0) {
724 nzFree(buf);
725 nzFree(chunk);
726 *data = emptyString;
727 *len = 0;
728 setError(MSG_NoRead, "file descriptor");
729 return false;
730 }
731
732 if (n > 0)
733 stringAndBytes(&buf, &length, chunk, n);
734 } while (n != 0);
735
736 nzFree(chunk);
737 buf = reallocString(buf, length + 2);
738 *data = buf;
739 *len = length;
740 return true;
741 } /* fdIntoMemory */
742
fileIntoMemory(const char * filename,char ** data,int * len)743 bool fileIntoMemory(const char *filename, char **data, int *len)
744 {
745 int fh;
746 char ftype = fileTypeByName(filename, false);
747 bool ret;
748 if (ftype && ftype != 'f' && ftype != 'p') {
749 setError(MSG_RegularFile, filename);
750 return false;
751 }
752 fh = open(filename, O_RDONLY | O_BINARY);
753 if (fh < 0) {
754 setError(MSG_NoOpen, filename);
755 return false;
756 }
757
758 ret = fdIntoMemory(fh, data, len);
759 if (ret == false)
760 setError(MSG_NoRead2, filename);
761
762 close(fh);
763 return ret;
764 } /* fileIntoMemory */
765
766 /* inverse of the above */
memoryOutToFile(const char * filename,const char * data,int len,int msgcreate,int msgwrite)767 bool memoryOutToFile(const char *filename, const char *data, int len,
768 /* specify the error messages */
769 int msgcreate, int msgwrite)
770 {
771 int fh =
772 open(filename, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, MODE_rw);
773 if (fh < 0) {
774 setError(msgcreate, filename, errno);
775 return false;
776 }
777 if (write(fh, data, len) < len) {
778 setError(msgwrite, filename, errno);
779 close(fh);
780 return false;
781 }
782 close(fh);
783 return true;
784 } /* memoryOutToFile */
785
786 // portable function to truncate to 0
truncate0(const char * filename,int fh)787 void truncate0(const char *filename, int fh)
788 {
789 #ifndef DOSLIKE
790 // unix is easy
791 if (fh < 0)
792 truncate(filename, 0l);
793 else
794 ftruncate(fh, 0l);
795 #else
796 int fh2;
797 if (fh >= 0)
798 lseek(fh, 0L, 0);
799 fh2 = open(filename, O_WRONLY | O_TRUNC);
800 if (fh2 >= 0) // it should be
801 close(fh2);
802 #endif
803 }
804
805 /* shift string to upper, lower, or mixed case */
806 /* action is u, l, or m. */
caseShift(char * s,char action)807 void caseShift(char *s, char action)
808 {
809 char c;
810 int mc = 0;
811 bool ws = true;
812
813 for (; (c = *s); ++s) {
814 if (action == 'u') {
815 if (isalphaByte(c))
816 *s = toupper(c);
817 continue;
818 }
819 if (action == 'l') {
820 if (isalphaByte(c))
821 *s = tolower(c);
822 continue;
823 }
824 /* mixed case left */
825 if (isalphaByte(c)) {
826 if (ws)
827 c = toupper(c);
828 else
829 c = tolower(c);
830 if (ws && c == 'M')
831 mc = 1;
832 else if (mc == 1 && c == 'c')
833 mc = 2;
834 else if (mc == 2) {
835 c = toupper(c);
836 mc = 0;
837 } else
838 mc = 0;
839 *s = c;
840 ws = false;
841 continue;
842 }
843 ws = true, mc = 0;
844 } /* loop */
845 } /* caseShift */
846
847 // foo-bar has to become fooBar
camelCase(char * s)848 void camelCase(char *s)
849 {
850 char *t, *w;
851 for (t = w = s; *t; ++t)
852 if (*t == '-' && isalpha(t[1]))
853 t[1] = toupper(t[1]);
854 else
855 *w++ = *t;
856 *w = 0;
857 }
858
859 /*********************************************************************
860 Manage files, directories, and terminal IO.
861 You'll see some conditional compilation when this program
862 is ported to other operating systems.
863 *********************************************************************/
864
865 /* Return the type of a file.
866 * Make it a capital letter if you are going through a link.
867 * I think this will work on Windows, not sure.
868 * But the link feature is Unix specific.
869 * Remember the stat structure for other things. */
870 struct stat this_stat;
871 static bool this_waslink, this_brokenlink;
872
fileTypeByName(const char * name,bool showlink)873 char fileTypeByName(const char *name, bool showlink)
874 {
875 bool islink = false;
876 char c;
877 int mode;
878
879 this_waslink = false;
880 this_brokenlink = false;
881
882 #ifdef DOSLIKE
883 if (stat(name, &this_stat)) {
884 this_brokenlink = true;
885 setError(MSG_NoAccess, name);
886 return 0;
887 }
888 mode = this_stat.st_mode & S_IFMT;
889 #else // !DOSLIKE
890
891 if (lstat(name, &this_stat)) {
892 this_brokenlink = true;
893 setError(MSG_NoAccess, name);
894 return 0;
895 }
896 mode = this_stat.st_mode & S_IFMT;
897 if (mode == S_IFLNK) { /* symbolic link */
898 islink = this_waslink = true;
899 /* If this fails, I'm guessing it's just a file. */
900 if (stat(name, &this_stat)) {
901 this_brokenlink = true;
902 return (showlink ? 'F' : 0);
903 }
904 mode = this_stat.st_mode & S_IFMT;
905 }
906 #endif // DOSLIKE y/n
907
908 c = 'f';
909 if (mode == S_IFDIR)
910 c = 'd';
911 #ifndef DOSLIKE
912 /* I don't think these are Windows constructs. */
913 if (mode == S_IFBLK)
914 c = 'b';
915 if (mode == S_IFCHR)
916 c = 'c';
917 if (mode == S_IFIFO)
918 c = 'p';
919 if (mode == S_IFSOCK)
920 c = 's';
921 #endif
922 if (islink & showlink)
923 c = toupper(c);
924 return c;
925 } /* fileTypeByName */
926
fileTypeByHandle(int fd)927 char fileTypeByHandle(int fd)
928 {
929 struct stat buf;
930 char c;
931 int mode;
932 if (fstat(fd, &buf)) {
933 setError(MSG_NoAccess, "handle");
934 return 0;
935 }
936 mode = buf.st_mode & S_IFMT;
937 c = 'f';
938 if (mode == S_IFDIR)
939 c = 'd';
940 #ifndef DOSLIKE
941 /* I don't think these are Windows constructs. */
942 if (mode == S_IFBLK)
943 c = 'b';
944 if (mode == S_IFCHR)
945 c = 'c';
946 if (mode == S_IFIFO)
947 c = 'p';
948 if (mode == S_IFSOCK)
949 c = 's';
950 #endif
951 return c;
952 } /* fileTypeByHandle */
953
fileSizeByName(const char * name)954 off_t fileSizeByName(const char *name)
955 {
956 struct stat buf;
957 if (stat(name, &buf)) {
958 setError(MSG_NoAccess, name);
959 return -1;
960 }
961 return buf.st_size;
962 } /* fileSizeByName */
963
fileSizeByHandle(int fd)964 off_t fileSizeByHandle(int fd)
965 {
966 struct stat buf;
967 if (fstat(fd, &buf)) {
968 /* should never happen */
969 return -1;
970 }
971 return buf.st_size;
972 } /* fileSizeByHandle */
973
fileTimeByName(const char * name)974 time_t fileTimeByName(const char *name)
975 {
976 struct stat buf;
977 if (stat(name, &buf)) {
978 setError(MSG_NoAccess, name);
979 return -1;
980 }
981 return buf.st_mtime;
982 } /* fileTimeByName */
983
conciseSize(size_t n)984 char *conciseSize(size_t n)
985 {
986 static char buf[32];
987 unsigned long u, v;
988 if (n >= 1000000000) {
989 u = n/1000000000;
990 v = n/100000000 % 10;
991 if(u >= 10 || !v)
992 sprintf(buf, "%luG", u);
993 else
994 sprintf(buf, "%lu.%0luG", u, v);
995 } else if (n >= 1000000) {
996 u = n/1000000;
997 v = n/100000 % 10;
998 if(u >= 10 || !v)
999 sprintf(buf, "%luM", u);
1000 else
1001 sprintf(buf, "%lu.%0luM", u, v);
1002 } else if (n >= 1000) {
1003 u = n/1000;
1004 v = n/100 % 10;
1005 if(u >= 10 || !v)
1006 sprintf(buf, "%luK", u);
1007 else
1008 sprintf(buf, "%lu.%0luK", u, v);
1009 } else {
1010 u = n;
1011 sprintf(buf, "%lu", u);
1012 }
1013 return buf;
1014 } /* conciseSize */
1015
conciseTime(time_t t)1016 char *conciseTime(time_t t)
1017 {
1018 static char buffer[20];
1019 static const char *const englishMonths[] = {
1020 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1021 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1022 };
1023 static const char *const frenchMonths[] = {
1024 "Janv", "Fév", "Mars", "Avr", "Mai", "Juin",
1025 "Juill", "Août", "Sept", "Oct", "Nov", "Déc",
1026 0
1027 };
1028 static const char *const portMonths[] = {
1029 "Jan", "Fev", "Mar", "Abr", "Mai", "Jun",
1030 "Jul", "Ago", "Set", "Out", "Nov", "Dec",
1031 };
1032 static const char *const germanMonths[] = {
1033 "Jan", "Feb", "Mär", "Apr", "Mai", "Jun",
1034 "Jul", "Aug", "Sep", "Okt", "Nov", "Dez",
1035 };
1036 static const char *const italMonths[] = {
1037 "Gen", "Feb", "Mar", "Apr", "Mag", "Giu",
1038 "Lug", "Ago", "Set", "Ott", "Nov", "Dic",
1039 0
1040 };
1041 static const char *const *const allMonths[] = { 0,
1042 englishMonths,
1043 frenchMonths,
1044 portMonths,
1045 englishMonths,
1046 germanMonths,
1047 englishMonths,
1048 italMonths,
1049 };
1050 struct tm *tm = localtime(&t);
1051 sprintf(buffer, "%s %2d %d %02d:%02d",
1052 allMonths[eb_lang][tm->tm_mon], tm->tm_mday, tm->tm_year + 1900,
1053 tm->tm_hour, tm->tm_min);
1054 return buffer;
1055 } /* conciseTime */
1056
1057 /* retain only characters l s t i k p m, for ls attributes */
lsattrChars(const char * buf,char * dest)1058 bool lsattrChars(const char *buf, char *dest)
1059 {
1060 bool rc = true;
1061 const char *s;
1062 char c, *t;
1063 #ifdef DOSLIKE
1064 static const char ok_chars[] = "lst";
1065 #else
1066 static const char ok_chars[] = "lstikpmy";
1067 #endif
1068 char used[26];
1069 memset(used, 0, sizeof(used));
1070 t = dest;
1071 for (s = buf; (c = *s); ++s) {
1072 if (isspaceByte(c))
1073 continue;
1074 if (!strchr(ok_chars, c)) {
1075 rc = false;
1076 continue;
1077 }
1078 if (used[c - 'a'])
1079 continue;
1080 used[c - 'a'] = 1;
1081 *t++ = c;
1082 }
1083 *t = 0;
1084 return rc;
1085 } /* lsattrChars */
1086
1087 /* expand the ls attributes for a file into a static string. */
1088 /* This assumes user/group names will not be too long. */
1089 /* Assumes we just called fileTypeByName. */
lsattr(const char * path,const char * flags)1090 char *lsattr(const char *path, const char *flags)
1091 {
1092 static char buf[200 + ABSPATH];
1093 char p[40];
1094 struct passwd *pwbuf;
1095 struct group *grpbuf;
1096 char *s;
1097 int l, modebits;
1098 char newpath[ABSPATH];
1099
1100 buf[0] = 0;
1101
1102 if (!path || !path[0] || !flags || !flags[0])
1103 return buf;
1104
1105 while (*flags) {
1106 if (buf[0])
1107 strcat(buf, " ");
1108 if (this_brokenlink && *flags != 'y') {
1109 strcat(buf, "~");
1110 ++flags;
1111 continue;
1112 }
1113
1114 switch (*flags) {
1115 case 't':
1116 strcat(buf, conciseTime(this_stat.st_mtime));
1117 break;
1118 case 'l':
1119 sprintf(p, "%lld", (long long)this_stat.st_size);
1120 #ifndef DOSLIKE
1121 p:
1122 #endif // #ifndef DOSLIKE
1123 strcat(buf, p);
1124 break;
1125 case 's':
1126 strcat(buf, conciseSize(this_stat.st_size));
1127 break;
1128 #ifndef DOSLIKE
1129 /* not sure any of these work under windows */
1130
1131 case 'i':
1132 sprintf(p, "%lu", (unsigned long)this_stat.st_ino);
1133 goto p;
1134 case 'k':
1135 sprintf(p, "%lu", (unsigned long)this_stat.st_nlink);
1136 goto p;
1137 case 'm':
1138 strcpy(p, "-");
1139 if (this_stat.st_rdev)
1140 sprintf(p, "%d/%d",
1141 (int)(this_stat.st_rdev >> 8),
1142 (int)(this_stat.st_rdev & 0xff));
1143 goto p;
1144 case 'p':
1145 s = buf + strlen(buf);
1146 pwbuf = getpwuid(this_stat.st_uid);
1147 if (pwbuf) {
1148 l = strlen(pwbuf->pw_name);
1149 if (l > 20)
1150 l = 20;
1151 strncpy(s, pwbuf->pw_name, l);
1152 s[l] = 0;
1153 } else
1154 sprintf(s, "%d", this_stat.st_uid);
1155 s += strlen(s);
1156 *s++ = ' ';
1157 grpbuf = getgrgid(this_stat.st_gid);
1158 if (grpbuf) {
1159 l = strlen(grpbuf->gr_name);
1160 if (l > 20)
1161 l = 20;
1162 strncpy(s, grpbuf->gr_name, l);
1163 s[l] = 0;
1164 } else
1165 sprintf(s, "%d", this_stat.st_gid);
1166 s += strlen(s);
1167 *s++ = ' ';
1168 modebits = this_stat.st_mode;
1169 modebits &= 07777;
1170 if (modebits & 07000)
1171 *s++ = '0' + (modebits >> 9);
1172 modebits &= 0777;
1173 *s++ = '0' + (modebits >> 6);
1174 modebits &= 077;
1175 *s++ = '0' + (modebits >> 3);
1176 modebits &= 7;
1177 *s++ = '0' + modebits;
1178 *s = 0;
1179 break;
1180
1181 case 'y':
1182 if (!this_waslink) {
1183 strcat(buf, "-");
1184 break;
1185 }
1186 /* yes it's a link, read the path */
1187 l = readlink(path, newpath, sizeof(newpath));
1188 if (l <= 0)
1189 strcat(buf, "...");
1190 else {
1191 s = buf + strlen(buf);
1192 strncpy(s, newpath, l);
1193 s[l] = 0;
1194 }
1195 break;
1196
1197 #endif
1198
1199 }
1200
1201 ++flags;
1202 }
1203
1204 return buf;
1205 } /* lsattr */
1206
1207 #ifdef DOSLIKE
ttySaveSettings(void)1208 void ttySaveSettings(void)
1209 {
1210 // TODO: Anything needed here for WIN32?
1211 isInteractive = _isatty(0);
1212 }
1213 #else // !#ifdef DOSLIKE
1214 static struct termios savettybuf;
ttySaveSettings(void)1215 void ttySaveSettings(void)
1216 {
1217 isInteractive = isatty(0);
1218 if (isInteractive) {
1219 if (tcgetattr(0, &savettybuf))
1220 i_printfExit(MSG_IoctlError);
1221 }
1222 } /* ttySaveSettings */
1223
ttyRestoreSettings(void)1224 static void ttyRestoreSettings(void)
1225 {
1226 if (isInteractive)
1227 tcsetattr(0, TCSANOW, &savettybuf);
1228 } /* ttyRestoreSettings */
1229
1230 /* put the tty in raw mode.
1231 * Review your Unix manual on termio.
1232 * min>0 time>0: return min chars, or as many as you have received
1233 * when time/10 seconds have elapsed between characters.
1234 * min>0 time=0: block until min chars are received.
1235 * min=0 time>0: return 1 char, or 0 if the timer expires.
1236 * min=0 time=0: nonblocking, return whatever chars have been received. */
ttyRaw(int charcount,int timeout,bool isecho)1237 static void ttyRaw(int charcount, int timeout, bool isecho)
1238 {
1239 struct termios buf = savettybuf; /* structure copy */
1240 buf.c_cc[VMIN] = charcount;
1241 buf.c_cc[VTIME] = timeout;
1242 buf.c_lflag &= ~(ICANON | ECHO);
1243 if (isecho)
1244 buf.c_lflag |= ECHO;
1245 tcsetattr(0, TCSANOW, &buf);
1246 } /* ttyRaw */
1247 #endif // #ifdef DOSLIKE y/n
1248
ttySetEcho(bool enable_echo)1249 void ttySetEcho(bool enable_echo)
1250 {
1251 #ifndef DOSLIKE
1252 struct termios termios;
1253
1254 if (!isInteractive)
1255 return;
1256
1257 tcgetattr(0, &termios);
1258 if (enable_echo) {
1259 termios.c_lflag |= ECHO;
1260 termios.c_lflag &= ~ECHONL;
1261 } else {
1262 termios.c_lflag &= ~ECHO;
1263 termios.c_lflag |= ECHONL;
1264 }
1265 tcsetattr(0, TCSANOW, &termios);
1266 #endif // #ifndef DOSLIKE
1267 }
1268
1269 #ifndef DOSLIKE
1270 /* simulate MSDOS getche() system call */
getche(void)1271 int getche(void)
1272 {
1273 char c;
1274 fflush(stdout);
1275 ttyRaw(1, 0, true);
1276 read(0, &c, 1);
1277 ttyRestoreSettings();
1278 return c;
1279 } /* getche */
1280
getch(void)1281 int getch(void)
1282 {
1283 char c;
1284 fflush(stdout);
1285 ttyRaw(1, 0, false);
1286 read(0, &c, 1);
1287 ttyRestoreSettings();
1288 return c;
1289 } /* getche */
1290
1291 #endif // #ifndef DOSLIKE
1292
getLetter(const char * s)1293 char getLetter(const char *s)
1294 {
1295 char c;
1296 while (true) {
1297 c = getch();
1298 if (strchr(s, c))
1299 break;
1300 printf("\a\b");
1301 fflush(stdout);
1302 }
1303 printf("%c", c);
1304 return c;
1305 } /* getLetter */
1306
1307 /* Parameters: message, default file name, must this file be new,
1308 * and can we except an input of white space,
1309 * that being converted to a single space. */
getFileName(int msg,const char * defname,bool isnew,bool ws)1310 char *getFileName(int msg, const char *defname, bool isnew, bool ws)
1311 {
1312 static char buf[ABSPATH];
1313 static char spacename[] = " ";
1314 int l;
1315 char *p;
1316 bool allspace;
1317
1318 while (true) {
1319 i_printf(msg);
1320 if (defname)
1321 printf("[%s] ", defname);
1322 fflush(stdout);
1323 if (!fgets(buf, sizeof(buf), stdin))
1324 exit(0);
1325 allspace = false;
1326 for (p = buf; isspaceByte(*p); ++p)
1327 if (*p == ' ')
1328 allspace = true;
1329 l = strlen(p);
1330 while (l && isspaceByte(p[l - 1]))
1331 --l;
1332 p[l] = 0;
1333 if (!l) {
1334 if (ws & allspace)
1335 return spacename;
1336 if (!defname)
1337 continue;
1338 /* make a copy just to be safe */
1339 l = strlen(defname);
1340 if (l >= ABSPATH)
1341 l = ABSPATH - 1;
1342 strncpy(buf, defname, l);
1343 buf[l] = 0;
1344 p = buf;
1345 } else
1346 defname = 0;
1347 if (isnew && fileTypeByName(p, false)) {
1348 i_printf(MSG_FileExists, p);
1349 defname = 0;
1350 continue;
1351 }
1352 return p;
1353 }
1354 } /* getFileName */
1355
1356 /* Protect a filename from expansion by the shell */
1357 static const char shellmeta[] = "\\\n\t |&;<>(){}#'\"~$*?";
shellProtectLength(const char * s)1358 int shellProtectLength(const char *s)
1359 {
1360 int l = 0;
1361 while (*s) {
1362 if (strchr(shellmeta, *s))
1363 ++l;
1364 ++l, ++s;
1365 }
1366 return l;
1367 } /* shellProtectLength */
1368
shellProtect(char * t,const char * s)1369 void shellProtect(char *t, const char *s)
1370 {
1371 while (*s) {
1372 if (strchr(shellmeta, *s))
1373 *t++ = '\\';
1374 *t++ = *s++;
1375 }
1376 } /* shellProtect */
1377
1378 /* loop through the files in a directory */
nextScanFile(const char * base)1379 const char *nextScanFile(const char *base)
1380 {
1381 static DIR *df;
1382 struct dirent *de;
1383 const char *s;
1384
1385 if (!df) {
1386 if (!base)
1387 base = ".";
1388 df = opendir(base);
1389 /* directory could be unreadable */
1390 if (!df) {
1391 i_puts(MSG_NoDirNoList);
1392 return 0;
1393 }
1394 }
1395
1396 while ((de = readdir(df))) {
1397 s = de->d_name;
1398 if (s[0] == '.') {
1399 if (stringEqual(s, "."))
1400 continue;
1401 if (!showHiddenFiles)
1402 continue;
1403 }
1404 return s;
1405 } /* end loop over files in directory */
1406
1407 closedir(df);
1408 df = 0;
1409 return 0;
1410 } /* nextScanFile */
1411
1412 /* compare routine for quicksort directory scan */
1413 static bool dir_reverse;
dircmp(const void * s,const void * t)1414 static int dircmp(const void *s, const void *t)
1415 {
1416 int rc = strcoll((const char *)((const struct lineMap *)s)->text,
1417 (const char *)((const struct lineMap *)t)->text);
1418 if (dir_reverse)
1419 rc = -rc;
1420 return rc;
1421 } /* dircmp */
1422
sortedDirList(const char * dir,struct lineMap ** map_p,int * count_p,int othersort,bool reverse)1423 bool sortedDirList(const char *dir, struct lineMap ** map_p, int *count_p,
1424 int othersort, bool reverse)
1425 {
1426 const char *f;
1427 int linecount = 0, cap;
1428 struct lineMap *t, *map;
1429
1430 cap = 128;
1431 map = t = (struct lineMap *)allocZeroMem(cap * LMSIZE);
1432
1433 while ((f = nextScanFile(dir))) {
1434 if (linecount == cap) {
1435 cap *= 2;
1436 map = (struct lineMap *)reallocMem(map, cap * LMSIZE);
1437 t = map + linecount;
1438 }
1439 /* leave room for @ / newline */
1440 t->text = (pst) allocMem(strlen(f) + 3);
1441 strcpy((char *)t->text, f);
1442 t->ds1 = t->ds2 = 0;
1443 ++t, ++linecount;
1444 }
1445
1446 *count_p = linecount;
1447 *map_p = map;
1448
1449 if (!linecount)
1450 return true;
1451
1452 // Sort the entries alphabetical,
1453 // unless we plan to sort them some other way.
1454 if (!othersort) {
1455 dir_reverse = reverse;
1456 qsort(map, linecount, LMSIZE, dircmp);
1457 }
1458
1459 return true;
1460 } /* sortedDirList */
1461
1462 /* Expand environment variables, then wild cards.
1463 * But the result should be one and only one file.
1464 * Return the new expanded line.
1465 * Neither the original line nore the new line is allocated.
1466 * They are static char buffers that are just plain long enough. */
1467
envExpand(const char * line,const char ** expanded)1468 static bool envExpand(const char *line, const char **expanded)
1469 {
1470 const char *s;
1471 char *t;
1472 const char *v; /* result of getenv call */
1473 bool inbrace; /* ${foo} */
1474 struct passwd *pw;
1475 const char *udir; /* user directory */
1476 int l;
1477 static char varline[ABSPATH];
1478 char var1[40];
1479
1480 /* quick check */
1481 if (line[0] != '~' && !strchr(line, '$')) {
1482 *expanded = line;
1483 return true;
1484 }
1485
1486 /* ok, need to crunch along */
1487 t = varline;
1488 s = line;
1489
1490 if (line[0] != '~')
1491 goto dollars;
1492
1493 l = 0;
1494 for (s = line + 1; isalnum(*s) || *s == '_'; ++s)
1495 ++l;
1496 if (l >= sizeof(var1) || isdigit(line[1]) || (*s && *s != '/')) {
1497 /* invalid syntax, put things back */
1498 s = line;
1499 goto dollars;
1500 }
1501
1502 udir = 0;
1503 strncpy(var1, line + 1, l);
1504 var1[l] = 0;
1505 #ifndef DOSLIKE
1506 if (l) {
1507 pw = getpwnam(var1);
1508 if (!pw) {
1509 setError(MSG_NoTilde, var1);
1510 return false;
1511 }
1512 if (pw->pw_dir && *pw->pw_dir)
1513 udir = pw->pw_dir;
1514 } else
1515 #endif
1516 udir = home;
1517 if (!udir) {
1518 s = line;
1519 goto dollars;
1520 }
1521 l = strlen(udir);
1522 if (l >= sizeof(varline))
1523 goto longline;
1524 strcpy(varline, udir);
1525 t = varline + l;
1526
1527 dollars:
1528 for (; *s; ++s) {
1529 if (t - varline == ABSPATH - 1) {
1530 longline:
1531 setError(MSG_ShellLineLong);
1532 return false;
1533 }
1534 if (*s == '\\' && s[1] == '$') {
1535 /* this $ is escaped */
1536 ++s;
1537 appendchar:
1538 *t++ = *s;
1539 continue;
1540 }
1541 if (*s != '$')
1542 goto appendchar;
1543
1544 /* this is $, see if it is $var or ${var} */
1545 inbrace = false;
1546 v = s + 1;
1547 if (*v == '{')
1548 inbrace = true, ++v;
1549 if (!isalphaByte(*v) && *v != '_')
1550 goto appendchar;
1551 l = 0;
1552 while (isalnumByte(*v) || *v == '_') {
1553 if (l == sizeof(var1) - 1)
1554 goto longline;
1555 var1[l++] = *v++;
1556 }
1557 var1[l] = 0;
1558 if (inbrace) {
1559 if (*v != '}')
1560 goto appendchar;
1561 ++v;
1562 }
1563 s = v - 1;
1564 v = getenv(var1);
1565 if (!v) {
1566 setError(MSG_NoEnvVar, var1);
1567 return false;
1568 }
1569 l = strlen(v);
1570 if (t - varline + l >= ABSPATH)
1571 goto longline;
1572 strcpy(t, v);
1573 t += l;
1574 }
1575 *t = 0;
1576
1577 *expanded = varline;
1578 return true;
1579 } /* envExpand */
1580
envFile(const char * line,const char ** expanded)1581 bool envFile(const char *line, const char **expanded)
1582 {
1583 static char line2[ABSPATH];
1584 const char *varline;
1585 const char *s;
1586 char *t;
1587 #ifndef DOSLIKE
1588 glob_t g;
1589 #endif // #ifndef DOSLIKE
1590 int rc, flags;
1591
1592 /* ` disables this stuff */
1593 /* but `` is a literal ` */
1594 if (line[0] == '`') {
1595 if (line[1] != '`') {
1596 *expanded = line + 1;
1597 return true;
1598 }
1599 ++line;
1600 }
1601
1602 if (!envExpand(line, &varline))
1603 return false;
1604
1605 #ifdef DOSLIKE
1606 *expanded = varline;
1607 return true; // TODO: WIN32: Expand like glob...
1608 #else // !#ifdef DOSLIKE
1609
1610 /* expanded the environment variables, if any, now time to glob */
1611 flags = GLOB_NOSORT;
1612 rc = glob(varline, flags, NULL, &g);
1613
1614 if (rc == GLOB_NOMATCH) {
1615 /* unescape the metas */
1616 t = line2;
1617 for (s = varline; *s; ++s) {
1618 if (*s == '\\' && s[1] && strchr("*?[", s[1]))
1619 ++s;
1620 *t++ = *s;
1621 }
1622 *t = 0;
1623 *expanded = line2;
1624 return true;
1625 }
1626
1627 if (rc) {
1628 /* some other syntax error, whereup we can't expand. */
1629 setError(MSG_ShellExpand);
1630 globfree(&g);
1631 return false;
1632 }
1633
1634 if (g.gl_pathc != 1) {
1635 setError(MSG_ShellManyMatch);
1636 globfree(&g);
1637 return false;
1638 }
1639
1640 /* looks good, if it isn't too long */
1641 s = g.gl_pathv[0];
1642 if (strlen(s) >= sizeof(line2)) {
1643 setError(MSG_ShellLineLong);
1644 globfree(&g);
1645 return false;
1646 }
1647
1648 strcpy(line2, s);
1649 globfree(&g);
1650 *expanded = line2;
1651 return true;
1652 #endif // #ifdef DOSLIKE y/n
1653
1654 } /* envFile */
1655
1656 /* Call the above routine if filename contains a slash,
1657 * or prepend the download directory if it does not.
1658 * If there is no download directory then always expand as above. */
envFileDown(const char * line,const char ** expanded)1659 bool envFileDown(const char *line, const char **expanded)
1660 {
1661 static char line2[MAXTTYLINE];
1662
1663 if (!downDir || strchr(line, '/'))
1664 /* we don't necessarily expect there to be a file here */
1665 return envFile(line, expanded);
1666
1667 if (strlen(downDir) + strlen(line) >= sizeof(line2) - 1) {
1668 setError(MSG_ShellLineLong);
1669 return false;
1670 }
1671
1672 sprintf(line2, "%s/%s", downDir, line);
1673 *expanded = line2;
1674 return true;
1675 } /* envFileDown */
1676
efopen(const char * name,const char * mode)1677 FILE *efopen(const char *name, const char *mode)
1678 {
1679 FILE *f;
1680
1681 if (name[0] == '-' && name[1] == 0) {
1682 if (*mode == 'r')
1683 return stdin;
1684 if (*mode == 'w' || *mode == 'a')
1685 return stdout;
1686 }
1687
1688 f = fopen(name, mode);
1689 if (f)
1690 return f;
1691
1692 if (*mode == 'r')
1693 i_printfExit(MSG_OpenFail, name);
1694 else if (*mode == 'w' || *mode == 'a')
1695 i_printfExit(MSG_CreateFail, name);
1696 else
1697 i_printfExit(MSG_InvalidFopen, mode);
1698 return 0;
1699 } /* efopen */
1700
eopen(const char * name,int mode,int perms)1701 int eopen(const char *name, int mode, int perms)
1702 {
1703 int fd;
1704 fd = open(name, mode, perms);
1705 if (fd >= 0)
1706 return fd;
1707 if (mode & O_WRONLY)
1708 i_printfExit(MSG_CreateFail, name);
1709 else
1710 i_printfExit(MSG_OpenFail, name);
1711 return -1;
1712 } /* eopen */
1713
appendFile(const char * fname,const char * message,...)1714 void appendFile(const char *fname, const char *message, ...)
1715 {
1716 FILE *f;
1717 va_list p;
1718 f = efopen(fname, "a");
1719 va_start(p, message);
1720 vfprintf(f, message, p);
1721 va_end(p);
1722 fprintf(f, "\n");
1723 fclose(f);
1724 } /* appendFile */
1725
1726 /* like the above, but no formatting */
appendFileNF(const char * filename,const char * msg)1727 void appendFileNF(const char *filename, const char *msg)
1728 {
1729 FILE *f = efopen(filename, "a");
1730 fprintf(f, "%s\n", msg);
1731 fclose(f);
1732 } /* appendFileNF */
1733
1734 /* Wrapper around system(). */
eb_system(const char * cmd,bool print_on_success)1735 int eb_system(const char *cmd, bool print_on_success)
1736 {
1737 int system_ret = system(cmd);
1738 if (system_ret != -1) {
1739 if (print_on_success)
1740 i_puts(MSG_OK);
1741 } else {
1742 i_printf(MSG_SystemCmdFail, system_ret);
1743 nl();
1744 }
1745 return system_ret;
1746 } /* eb_system */
1747