1 /* util.c
2 */
3 /* This software is copyrighted as detailed in the LICENSE file. */
4
5
6 #include "EXTERN.h"
7 #include "common.h"
8 #include "final.h"
9 #include "term.h"
10 #include "list.h"
11 #include "hash.h"
12 #include "ngdata.h"
13 #include "nntpclient.h"
14 #include "datasrc.h"
15 #include "nntp.h"
16 #include "nntpauth.h"
17 #include "intrp.h"
18 #include "env.h"
19 #include "util2.h"
20 #include "only.h"
21 #include "search.h"
22 #ifdef I_SYS_WAIT
23 #include <sys/wait.h>
24 #endif
25 #ifdef MSDOS
26 #include <process.h>
27 #endif
28 #ifdef SCAN
29 #include "scan.h"
30 #include "smisc.h" /* s_default_cmd */
31 #endif
32 #include "univ.h"
33 #include "INTERN.h"
34 #include "util.ih"
35 #include "util.h"
36
37 #ifdef UNION_WAIT
38 typedef union wait WAIT_STATUS;
39 #else
40 typedef int WAIT_STATUS;
41 #endif
42
43 #ifndef USE_DEBUGGING_MALLOC
44 static char nomem[] = "trn: out of memory!\n";
45 #endif
46
47 static char null_export[] = "_=X";/* Just in case doshell precedes util_init */
48
49 static char* newsactive_export = null_export + 2;
50 static char* grpdesc_export = null_export + 2;
51 static char* quotechars_export = null_export + 2;
52 #ifdef SUPPORT_NNTP
53 static char* nntpserver_export = null_export + 2;
54 static char* nntpfds_export = null_export + 2;
55 #ifdef USE_GENAUTH
56 static char* nntpauth_export = null_export + 2;
57 #endif
58 static char* nntpforce_export = null_export + 2;
59 #endif
60
61 void
util_init()62 util_init()
63 {
64 extern char patchlevel[];
65 char* cp;
66 int i;
67 for (i = 0, cp = buf; i < 512; i++)
68 *cp++ = 'X';
69 *cp = '\0';
70 newsactive_export = export("NEWSACTIVE", buf);
71 grpdesc_export = export("NEWSDESCRIPTIONS", buf);
72 #ifdef SUPPORT_NNTP
73 nntpserver_export = export("NNTPSERVER", buf);
74 #endif
75 buf[64] = '\0';
76 quotechars_export = export("QUOTECHARS",buf);
77 #ifdef SUPPORT_NNTP
78 nntpfds_export = export("NNTPFDS", buf);
79 #ifdef USE_GENAUTH
80 nntpauth_export = export("NNTP_AUTH_FDS", buf);
81 #endif
82 buf[3] = '\0';
83 nntpforce_export = export("NNTP_FORCE_AUTH", buf);
84 #endif
85
86 for (cp = patchlevel; isspace(*cp); cp++) ;
87 export("TRN_VERSION", cp);
88 }
89
90 /* fork and exec a shell command */
91
92 int
doshell(shell,s)93 doshell(shell,s)
94 char* shell;
95 char* s;
96 {
97 #ifndef MSDOS
98 WAIT_STATUS status;
99 pid_t pid, w;
100 #endif
101 int ret;
102
103 xmouse_off();
104
105 #ifdef SIGTSTP
106 sigset(SIGTSTP,SIG_DFL);
107 sigset(SIGTTOU,SIG_DFL);
108 sigset(SIGTTIN,SIG_DFL);
109 #endif
110 #ifdef SUPPORT_NNTP
111 if (datasrc && (datasrc->flags & DF_REMOTE)) {
112 #ifdef USE_GENAUTH
113 if (export_nntp_fds) {
114 if (!nntplink.rd_fp) {
115 if (nntp_command("DATE") <= 0 || nntp_check() < 0)
116 finalize(1); /*$$*/
117 }
118 sprintf(buf,"%d.%d.%d",(int)fileno(nntplink.rd_fp),
119 (int)fileno(nntplink.wr_fp),nntplink.cookiefd);
120 re_export(nntpauth_export, buf, 512);
121 }
122 else
123 un_export(nntpauth_export);
124 #endif
125 if (!export_nntp_fds || !nntplink.rd_fp)
126 un_export(nntpfds_export);
127 else {
128 sprintf(buf,"%d.%d",(int)fileno(nntplink.rd_fp),
129 (int)fileno(nntplink.wr_fp));
130 re_export(nntpfds_export, buf, 64);
131 }
132 re_export(nntpserver_export,datasrc->newsid,512);
133 if (datasrc->nntplink.flags & NNTP_FORCE_AUTH_NEEDED)
134 re_export(nntpforce_export,"yes",3);
135 else
136 un_export(nntpforce_export);
137 if (datasrc->auth_user) {
138 int fd;
139 if ((fd = open(nntp_auth_file, O_WRONLY|O_CREAT, 0600)) >= 0) {
140 write(fd, datasrc->auth_user, strlen(datasrc->auth_user));
141 write(fd, "\n", 1);
142 if (datasrc->auth_pass) {
143 write(fd, datasrc->auth_pass, strlen(datasrc->auth_pass));
144 write(fd, "\n", 1);
145 }
146 close(fd);
147 }
148 }
149 if (nntplink.port_number) {
150 int len = strlen(nntpserver_export);
151 sprintf(buf,";%d",nntplink.port_number);
152 if (len + (int)strlen(buf) < 511)
153 strcpy(nntpserver_export+len, buf);
154 }
155 if (datasrc->act_sf.fp)
156 re_export(newsactive_export, datasrc->extra_name, 512);
157 else
158 re_export(newsactive_export, "none", 512);
159 } else {
160 #ifdef SUPPORT_NNTP
161 un_export(nntpfds_export);
162 #ifdef USE_GENAUTH
163 un_export(nntpauth_export);
164 #endif
165 un_export(nntpserver_export);
166 un_export(nntpforce_export);
167 #endif
168 if (datasrc)
169 re_export(newsactive_export, datasrc->newsid, 512);
170 else
171 un_export(newsactive_export);
172 }
173 #else
174 if (datasrc)
175 re_export(newsactive_export, datasrc->newsid, 512);
176 else
177 un_export(newsactive_export);
178 #endif
179 if (datasrc)
180 re_export(grpdesc_export, datasrc->grpdesc, 512);
181 else
182 un_export(grpdesc_export);
183 interp(buf,64-1+2,"%I");
184 buf[strlen(buf)-1] = '\0';
185 re_export(quotechars_export, buf+1, 64);
186 if (shell == NULL && (shell = getval("SHELL",NULL)) == NULL)
187 shell = PREFSHELL;
188 termlib_reset();
189 #ifdef MSDOS
190 status = spawnl(P_WAIT, shell, shell, "/c", s, (char*)NULL);
191 #else
192 if ((pid = vfork()) == 0) {
193 #ifdef SUPPORT_NNTP
194 if (datasrc && (datasrc->flags & DF_REMOTE)) {
195 int i;
196 /* This is necessary to keep the bourne shell from puking */
197 for (i = 3; i < 10; ++i) {
198 if (nntplink.rd_fp
199 && (i == fileno(nntplink.rd_fp)
200 || i == fileno(nntplink.wr_fp)))
201 continue;
202 #ifdef USE_GENAUTH
203 if (i == nntplink.cookiefd)
204 continue;
205 #endif
206 close(i);
207 }
208 }
209 #endif /* SUPPORT_NNTP */
210 if (nowait_fork) {
211 close(1);
212 close(2);
213 dup(open("/dev/null",1));
214 }
215
216 if (*s)
217 execl(shell, shell, "-c", s, (char*)NULL);
218 else
219 execl(shell, shell, (char*)NULL, (char*)NULL, (char*)NULL);
220 _exit(127);
221 }
222 sigignore(SIGINT);
223 #ifdef SIGQUIT
224 sigignore(SIGQUIT);
225 #endif
226 waiting = TRUE;
227 while ((w = wait(&status)) != pid)
228 if (w == -1 && errno != EINTR)
229 break;
230 if (w == -1)
231 ret = -1;
232 else
233 #ifdef USE_WIFSTAT
234 ret = WEXITSTATUS(status);
235 #else
236 #ifdef UNION_WAIT
237 ret = status.w_status >> 8;
238 #else
239 ret = status;
240 #endif /* UNION_WAIT */
241 #endif /* USE_WIFSTAT */
242 #endif /* !MSDOS */
243 termlib_init();
244 xmouse_check();
245 waiting = FALSE;
246 sigset(SIGINT,int_catcher);
247 #ifdef SIGQUIT
248 sigset(SIGQUIT,SIG_DFL);
249 #endif
250 #ifdef SIGTSTP
251 sigset(SIGTSTP,stop_catcher);
252 sigset(SIGTTOU,stop_catcher);
253 sigset(SIGTTIN,stop_catcher);
254 #endif
255 #ifdef SUPPORT_NNTP
256 if (datasrc && datasrc->auth_user)
257 UNLINK(nntp_auth_file);
258 #endif
259 return ret;
260 }
261
262 /* paranoid version of malloc */
263
264 #ifndef USE_DEBUGGING_MALLOC
265 char*
safemalloc(size)266 safemalloc(size)
267 MEM_SIZE size;
268 {
269 char* ptr;
270
271 ptr = malloc(size ? size : (MEM_SIZE)1);
272 if (!ptr) {
273 fputs(nomem,stdout) FLUSH;
274 sig_catcher(0);
275 }
276 return ptr;
277 }
278 #endif
279
280 /* paranoid version of realloc. If where is NULL, call malloc */
281
282 #ifndef USE_DEBUGGING_MALLOC
283 char*
saferealloc(where,size)284 saferealloc(where,size)
285 char* where;
286 MEM_SIZE size;
287 {
288 char* ptr;
289
290 if (!where)
291 ptr = malloc(size ? size : (MEM_SIZE)1);
292 else
293 ptr = realloc(where, size ? size : (MEM_SIZE)1);
294 if (!ptr) {
295 fputs(nomem,stdout) FLUSH;
296 sig_catcher(0);
297 }
298 return ptr;
299 }
300 #endif /* !USE_DEBUGGING_MALLOC */
301
302 /* safe version of string concatenate, with \n deletion and space padding */
303
304 char*
safecat(to,from,len)305 safecat(to,from,len)
306 char* to;
307 register char* from;
308 register int len;
309 {
310 register char* dest = to;
311
312 len--; /* leave room for null */
313 if (*dest) {
314 while (len && *dest++) len--;
315 if (len) {
316 len--;
317 *(dest-1) = ' ';
318 }
319 }
320 if (from)
321 while (len && (*dest++ = *from++)) len--;
322 if (len)
323 dest--;
324 if (*(dest-1) == '\n')
325 dest--;
326 *dest = '\0';
327 return to;
328 }
329
330 /* effective access */
331
332 #ifdef SETUIDGID
333 int
eaccess(filename,mod)334 eaccess(filename, mod)
335 char* filename;
336 int mod;
337 {
338 int protection, euid;
339
340 mod &= 7; /* remove extraneous garbage */
341 if (stat(filename, &filestat) < 0)
342 return -1;
343 euid = geteuid();
344 if (euid == ROOTID)
345 return 0;
346 protection = 7 & ( filestat.st_mode >> (filestat.st_uid == euid ?
347 6 : (filestat.st_gid == getegid() ? 3 : 0)) );
348 if ((mod & protection) == mod)
349 return 0;
350 errno = EACCES;
351 return -1;
352 }
353 #endif
354
355 /*
356 * Get working directory
357 */
358 char*
trn_getwd(buf,buflen)359 trn_getwd(buf, buflen)
360 char* buf;
361 int buflen;
362 {
363 char* ret;
364
365 #ifdef HAS_GETCWD
366 ret = getcwd(buf, buflen);
367 #else
368 ret = trn_getcwd(buf, buflen);
369 #endif
370 if (!ret) {
371 printf("Cannot determine current working directory!\n") FLUSH;
372 finalize(1);
373 }
374 #ifdef MSDOS
375 strlwr(buf);
376 while ((buf = index(buf,'\\')) != NULL)
377 *buf++ = '/';
378 #endif
379 return ret;
380 }
381
382 #ifndef HAS_GETCWD
383 static char*
trn_getcwd(buf,len)384 trn_getcwd(buf, len)
385 char* buf;
386 int len;
387 {
388 char* ret;
389 #ifdef HAS_GETWD
390 buf[len-1] = 0;
391 ret = getwd(buf);
392 if (buf[len-1]) {
393 /* getwd() overwrote the end of the buffer */
394 printf("getwd() buffer overrun!\n") FLUSH;
395 finalize(1);
396 }
397 #else
398 FILE* popen();
399 FILE* pipefp;
400 char* nl;
401
402 if ((pipefp = popen("/bin/pwd","r")) == NULL) {
403 printf("Can't popen /bin/pwd\n") FLUSH;
404 return NULL;
405 }
406 buf[0] = 0;
407 fgets(ret = buf, len, pipefp);
408 if (pclose(pipefp) == EOF) {
409 printf("Failed to run /bin/pwd\n") FLUSH;
410 return NULL;
411 }
412 if (!buf[0]) {
413 printf("/bin/pwd didn't output anything\n") FLUSH;
414 return NULL;
415 }
416 if ((nl = index(buf, '\n')) != NULL)
417 *nl = '\0';
418 #endif
419 return ret;
420 }
421 #endif
422
423 /* just like fgets but will make bigger buffer as necessary */
424
425 char*
get_a_line(buffer,buffer_length,realloc_ok,fp)426 get_a_line(buffer,buffer_length,realloc_ok,fp)
427 char* buffer;
428 register int buffer_length;
429 bool_int realloc_ok;
430 FILE* fp;
431 {
432 register int bufix = 0;
433 register int nextch;
434
435 do {
436 if (bufix >= buffer_length) {
437 buffer_length *= 2;
438 if (realloc_ok) { /* just grow in place, if possible */
439 buffer = saferealloc(buffer,(MEM_SIZE)buffer_length+1);
440 }
441 else {
442 char* tmp = safemalloc((MEM_SIZE)buffer_length+1);
443 strncpy(tmp,buffer,buffer_length/2);
444 buffer = tmp;
445 realloc_ok = TRUE;
446 }
447 }
448 if ((nextch = getc(fp)) == EOF) {
449 if (!bufix)
450 return NULL;
451 break;
452 }
453 buffer[bufix++] = (char)nextch;
454 } while (nextch && nextch != '\n');
455 buffer[bufix] = '\0';
456 len_last_line_got = bufix;
457 buflen_last_line_got = buffer_length;
458 return buffer;
459 }
460
461 int
makedir(dirname,nametype)462 makedir(dirname,nametype)
463 register char* dirname;
464 int nametype;
465 {
466 #ifdef MAKEDIR
467 register char* end;
468 register char* s;
469 # ifdef HAS_MKDIR
470 int status = 0;
471 # else
472 char tmpbuf[1024];
473 register char* tbptr = tmpbuf+5;
474 # endif
475
476 for (end = dirname; *end; end++) ; /* find the end */
477 if (nametype == MD_FILE) { /* not to create last component? */
478 for (--end; end != dirname && *end != '/'; --end) ;
479 if (*end != '/')
480 return 0; /* nothing to make */
481 *end = '\0'; /* isolate file name */
482 }
483 # ifndef HAS_MKDIR
484 strcpy(tmpbuf,"mkdir");
485 # endif
486
487 s = end;
488 for (;;) {
489 if (stat(dirname,&filestat) >= 0 && S_ISDIR(filestat.st_mode)) {
490 /* does this much exist as a dir? */
491 *s = '/'; /* mark this as existing */
492 break;
493 }
494 s = rindex(dirname,'/'); /* shorten name */
495 if (!s) /* relative path! */
496 break; /* hope they know what they are doing */
497 *s = '\0'; /* mark as not existing */
498 }
499
500 for (s=dirname; s <= end; s++) { /* this is grody but efficient */
501 if (!*s) { /* something to make? */
502 # ifdef HAS_MKDIR
503 status = status || mkdir(dirname,0777);
504 # else
505 sprintf(tbptr," %s",dirname);
506 tbptr += strlen(tbptr); /* make it, sort of */
507 # endif
508 *s = '/'; /* mark it made */
509 }
510 }
511 if (nametype == MD_DIR) /* don't need final slash unless */
512 *end = '\0'; /* a filename follows the dir name */
513
514 # ifdef HAS_MKDIR
515 return status;
516 # else
517 return (tbptr==tmpbuf+5 ? 0 : doshell(sh,tmpbuf));/* exercise our faith */
518 # endif
519 #else
520 sprintf(cmd_buf,"%s %s %d", filexp(DIRMAKER), dirname, nametype);
521 return doshell(sh,cmd_buf);
522 #endif
523 }
524
525 void
notincl(feature)526 notincl(feature)
527 char* feature;
528 {
529 printf("\nNo room for feature \"%s\" on this machine.\n",feature) FLUSH;
530 }
531
532 /* grow a static string to at least a certain length */
533
534 void
growstr(strptr,curlen,newlen)535 growstr(strptr,curlen,newlen)
536 char** strptr;
537 int* curlen;
538 int newlen;
539 {
540 if (newlen > *curlen) { /* need more room? */
541 if (*curlen)
542 *strptr = saferealloc(*strptr,(MEM_SIZE)newlen);
543 else
544 *strptr = safemalloc((MEM_SIZE)newlen);
545 *curlen = newlen;
546 }
547 }
548
549 void
setdef(buffer,dflt)550 setdef(buffer,dflt)
551 char* buffer;
552 char* dflt;
553 {
554 #ifdef SCAN
555 s_default_cmd = FALSE;
556 #endif
557 univ_default_cmd = FALSE;
558 if (*buffer == ' '
559 #ifndef STRICTCR
560 || *buffer == '\n' || *buffer == '\r'
561 #endif
562 ) {
563 #ifdef SCAN
564 s_default_cmd = TRUE;
565 #endif
566 univ_default_cmd = TRUE;
567 if (*dflt == '^' && isupper(dflt[1]))
568 pushchar(Ctl(dflt[1]));
569 else
570 pushchar(*dflt);
571 getcmd(buffer);
572 }
573 }
574
575 #ifndef NO_FILELINKS
576 void
safelink(old,new)577 safelink(old, new)
578 char* old;
579 char* new;
580 {
581 #if 0
582 extern int sys_nerr;
583 extern char* sys_errlist[];
584 #endif
585
586 if (link(old,new)) {
587 printf("Can't link backup (%s) to .newsrc (%s)\n", old, new) FLUSH;
588 #if 0
589 if (errno>0 && errno<sys_nerr)
590 printf("%s\n", sys_errlist[errno]);
591 #endif
592 finalize(1);
593 }
594 }
595 #endif
596
597 #ifndef HAS_STRSTR
598 char*
trn_strstr(s1,s2)599 trn_strstr(s1, s2)
600 char* s1;
601 char* s2;
602 {
603 register char* p = s1;
604 register int len = strlen(s2);
605
606 for ( ; (p = index(p, *s2)) != NULL; p++)
607 if (strnEQ(p, s2, len))
608 return p;
609 return NULL;
610 }
611 #endif /* !HAS_STRSTR */
612
613 /* attempts to verify a cryptographic signature. */
614 void
verify_sig()615 verify_sig()
616 {
617 int i;
618
619 printf("\n");
620 /* RIPEM */
621 i = doshell(sh,filexp("grep -s \"BEGIN PRIVACY-ENHANCED MESSAGE\" %A"));
622 if (!i) { /* found RIPEM */
623 i = doshell(sh,filexp(getval("VERIFY_RIPEM",VERIFY_RIPEM)));
624 printf("\nReturned value: %d\n",i) FLUSH;
625 return;
626 }
627 /* PGP */
628 i = doshell(sh,filexp("grep -s \"BEGIN PGP\" %A"));
629 if (!i) { /* found PGP */
630 i = doshell(sh,filexp(getval("VERIFY_PGP",VERIFY_PGP)));
631 printf("\nReturned value: %d\n",i) FLUSH;
632 return;
633 }
634 printf("No PGP/RIPEM signatures detected.\n") FLUSH;
635 }
636
637 double
current_time()638 current_time()
639 {
640 #ifdef HAS_GETTIMEOFDAY
641 Timeval t;
642 (void) gettimeofday(&t, (struct timezone*)NULL);
643 return (double)t.tv_usec / 1000000. + t.tv_sec;
644 #else
645 # ifdef HAS_FTIME
646 Timeval t;
647 ftime(&t);
648 return (double)t.millitm / 1000. + t.time;
649 # else
650 return (double)time((time_t*)NULL);
651 # endif
652 #endif
653 }
654
655 time_t
text2secs(s,defSecs)656 text2secs(s, defSecs)
657 char* s;
658 time_t defSecs;
659 {
660 time_t secs = 0;
661 time_t item;
662
663 if (!isdigit(*s)) {
664 if (*s == 'm' || *s == 'M') /* "missing" */
665 return 2;
666 if (*s == 'y' || *s == 'Y') /* "yes" */
667 return defSecs;
668 return secs; /* "never" */
669 }
670 do {
671 item = atol(s);
672 while (isdigit(*s)) s++;
673 while (isspace(*s)) s++;
674 if (isalpha(*s)) {
675 switch (*s) {
676 case 'd': case 'D':
677 item *= 24 * 60L;
678 break;
679 case 'h': case 'H':
680 item *= 60L;
681 break;
682 case 'm': case 'M':
683 break;
684 default:
685 item = 0;
686 break;
687 }
688 while (isalpha(*s)) s++;
689 if (*s == ',') s++;
690 while (isspace(*s)) s++;
691 }
692 secs += item;
693 } while (isdigit(*s));
694
695 return secs * 60;
696 }
697
698 char*
secs2text(secs)699 secs2text(secs)
700 time_t secs;
701 {
702 char* s = buf;
703 int items;
704
705 if (!secs || (secs & 1))
706 return "never";
707 if (secs & 2)
708 return "missing";
709
710 secs /= 60;
711 if (secs >= 24L * 60) {
712 items = (int)(secs / (24*60));
713 secs = secs % (24*60);
714 sprintf(s, "%d day%s, ", items, PLURAL(items));
715 s += strlen(s);
716 }
717 if (secs >= 60L) {
718 items = (int)(secs / 60);
719 secs = secs % 60;
720 sprintf(s, "%d hour%s, ", items, PLURAL(items));
721 s += strlen(s);
722 }
723 if (secs) {
724 sprintf(s, "%d minute%s, ", (int)secs, PLURAL(items));
725 s += strlen(s);
726 }
727 s[-2] = '\0';
728 return buf;
729 }
730
731 /* returns a saved string representing a unique temporary filename */
732 char*
temp_filename()733 temp_filename()
734 {
735 static int tmpfile_num = 0;
736 char tmpbuf[CBUFLEN];
737 extern long our_pid;
738 sprintf(tmpbuf,"%s/trn%d.%ld",tmpdir,tmpfile_num++,our_pid);
739 return savestr(tmpbuf);
740 }
741
742 #ifdef SUPPORT_NNTP
743 char*
get_auth_user()744 get_auth_user()
745 {
746 return datasrc->auth_user;
747 }
748 #endif
749
750 #ifdef SUPPORT_NNTP
751 char*
get_auth_pass()752 get_auth_pass()
753 {
754 return datasrc->auth_pass;
755 }
756 #endif
757
758 #if defined(USE_GENAUTH) && defined(SUPPORT_NNTP)
759 char*
get_auth_command()760 get_auth_command()
761 {
762 return datasrc->auth_command;
763 }
764 #endif
765
766 char**
prep_ini_words(words)767 prep_ini_words(words)
768 INI_WORDS words[];
769 {
770 register int checksum;
771 char* cp = (char*)INI_VALUES(words);
772 if (!cp) {
773 int i;
774 for (i = 1; words[i].item != NULL; i++) {
775 if (*words[i].item == '*') {
776 words[i].checksum = -1;
777 continue;
778 }
779 checksum = 0;
780 for (cp = words[i].item; *cp; cp++)
781 checksum += (isupper(*cp)? tolower(*cp) : *cp);
782 words[i].checksum = (checksum << 8) + (cp - words[i].item);
783 }
784 words[0].checksum = i;
785 words[0].help_str = cp = safemalloc(i * sizeof (char*));
786 }
787 bzero(cp, INI_LEN(words) * sizeof (char*));
788 return (char**)cp;
789 }
790
791 void
unprep_ini_words(words)792 unprep_ini_words(words)
793 INI_WORDS words[];
794 {
795 free((char*)INI_VALUES(words));
796 words[0].checksum = 0;
797 words[0].help_str = NULL;
798 }
799
800 void
prep_ini_data(cp,filename)801 prep_ini_data(cp,filename)
802 char* cp;
803 char* filename;
804 {
805 char* t = cp;
806
807 #ifdef DEBUG
808 if (debug & DEB_RCFILES)
809 printf("Read %d bytes from %s\n",strlen(cp),filename);
810 #endif
811
812 while (*cp) {
813 while (isspace(*cp)) cp++;
814
815 if (*cp == '[') {
816 char* s = t;
817 do {
818 *t++ = *cp++;
819 } while (*cp && *cp != ']' && *cp != '\n');
820 if (*cp == ']' && t != s) {
821 *t++ = '\0';
822 cp++;
823 if (parse_string(&t, &cp))
824 cp++;
825
826 while (*cp) {
827 while (isspace(*cp)) cp++;
828 if (*cp == '[')
829 break;
830 if (*cp == '#')
831 s = cp;
832 else {
833 s = t;
834 while (*cp && *cp != '\n') {
835 if (*cp == '=')
836 break;
837 if (isspace(*cp)) {
838 if (s == t || t[-1] != ' ')
839 *t++ = ' ';
840 cp++;
841 }
842 else
843 *t++ = *cp++;
844 }
845 if (*cp == '=' && t != s) {
846 while (t != s && isspace(t[-1])) t--;
847 *t++ = '\0';
848 cp++;
849 if (parse_string(&t, &cp))
850 s = NULL;
851 else
852 s = cp;
853 }
854 else
855 s = cp;
856 }
857 cp++;
858 if (s)
859 for (cp = s; *cp && *cp++ != '\n'; ) ;
860 }
861 }
862 else {
863 *t = '\0';
864 printf("Invalid section in %s: %s\n", filename, s);
865 t = s;
866 while (*cp && *cp++ != '\n') ;
867 }
868 }
869 else
870 while (*cp && *cp++ != '\n') ;
871 }
872 *t = '\0';
873 }
874
875 bool
parse_string(to,from)876 parse_string(to, from)
877 char** to;
878 char** from;
879 {
880 char inquote = 0;
881 char* t = *to;
882 char* f = *from;
883 char* s;
884
885 while (isspace(*f) && *f != '\n') f++;
886
887 for (s = t; *f; f++) {
888 if (inquote) {
889 if (*f == inquote) {
890 inquote = 0;
891 s = t;
892 continue;
893 }
894 }
895 else if (*f == '\n')
896 break;
897 else if (*f == '\'' || *f == '"') {
898 inquote = *f;
899 continue;
900 }
901 else if (*f == '#') {
902 while (*++f && *f != '\n') ;
903 break;
904 }
905 if (*f == '\\') {
906 if (*++f == '\n')
907 continue;
908 f = interp_backslash(t, f);
909 t++;
910 }
911 else
912 *t++ = *f;
913 }
914 #if 0
915 if (inquote)
916 printf("Unbalanced quotes.\n");
917 #endif
918 inquote = (*f != '\0');
919
920 while (t != s && isspace(t[-1])) t--;
921 *t++ = '\0';
922
923 *to = t;
924 *from = f;
925
926 return inquote; /* return TRUE if the string ended with a newline */
927 }
928
929 char*
next_ini_section(cp,section,cond)930 next_ini_section(cp,section,cond)
931 char* cp;
932 char** section;
933 char** cond;
934 {
935 while (*cp != '[') {
936 if (!*cp)
937 return NULL;
938 cp += strlen(cp) + 1;
939 cp += strlen(cp) + 1;
940 }
941 *section = cp+1;
942 cp += strlen(cp) + 1;
943 *cond = cp;
944 cp += strlen(cp) + 1;
945 #ifdef DEBUG
946 if (debug & DEB_RCFILES)
947 printf("Section [%s] (condition: %s)\n",*section,
948 **cond? *cond : "<none>");
949 #endif
950 return cp;
951 }
952
953 char*
parse_ini_section(cp,words)954 parse_ini_section(cp, words)
955 char* cp;
956 INI_WORDS words[];
957 {
958 register int checksum;
959 register char* s;
960 char** values = prep_ini_words(words);
961 int i;
962
963 if (!*cp)
964 return NULL;
965
966 while (*cp && *cp != '[') {
967 checksum = 0;
968 for (s = cp; *s; s++) {
969 if (isupper(*s))
970 *s = tolower(*s);
971 checksum += *s;
972 }
973 checksum = (checksum << 8) + (s++ - cp);
974 if (*s) {
975 for (i = 1; words[i].checksum; i++) {
976 if (words[i].checksum == checksum
977 && strcaseEQ(cp,words[i].item)) {
978 values[i] = s;
979 break;
980 }
981 }
982 if (!words[i].checksum)
983 printf("Unknown option: `%s'.\n",cp);
984 cp = s + strlen(s) + 1;
985 }
986 else
987 cp = s + 1;
988 }
989
990 #ifdef DEBUG
991 if (debug & DEB_RCFILES) {
992 printf("Ini_words: %s\n", words[0].item);
993 for (i = 1; words[i].checksum; i++)
994 if (values[i])
995 printf("%s=%s\n",words[i].item,values[i]);
996 }
997 #endif
998
999 return cp;
1000 }
1001
1002 bool
check_ini_cond(cond)1003 check_ini_cond(cond)
1004 char* cond;
1005 {
1006 int not, equal, upordown, num;
1007 char* s;
1008 cond = dointerp(buf,sizeof buf,cond,"!=<>",(char*)NULL);
1009 s = buf + strlen(buf);
1010 while (s != buf && isspace(s[-1])) s--;
1011 *s = '\0';
1012 if ((not = (*cond == '!')) != 0)
1013 cond++;
1014 if ((upordown = (*cond=='<'? -1: (*cond=='>'? 1:0))) != 0)
1015 cond++;
1016 if ((equal = (*cond == '=')) != 0)
1017 cond++;
1018 while (isspace(*cond)) cond++;
1019 if (upordown) {
1020 num = atoi(cond) - atoi(buf);
1021 if (!((equal && !num) || (upordown * num < 0)) ^ not)
1022 return FALSE;
1023 }
1024 else if (equal) {
1025 COMPEX condcompex;
1026 init_compex(&condcompex);
1027 if ((s = compile(&condcompex,cond,TRUE,TRUE)) != NULL) {
1028 /*warning(s)*/;
1029 equal = FALSE;
1030 }
1031 else
1032 equal = execute(&condcompex,buf) != NULL;
1033 free_compex(&condcompex);
1034 return equal;
1035 }
1036 else
1037 return FALSE;
1038 return TRUE;
1039 }
1040
1041 /* $$ might get replaced soonish... */
1042 /* Ask for a single character (improve the prompt?) */
1043 char
menu_get_char()1044 menu_get_char()
1045 {
1046 printf("Enter your choice: ");
1047 fflush(stdout);
1048 eat_typeahead();
1049 getcmd(buf);
1050 printf("%c\n",*buf) FLUSH;
1051 return(*buf);
1052 }
1053
1054 /* NOTE: kfile.c uses its own editor function */
1055 /* used in a few places, now centralized */
1056 int
edit_file(fname)1057 edit_file(fname)
1058 char* fname;
1059 {
1060 int r = -1;
1061
1062 if (!fname || !*fname)
1063 return r;
1064
1065 /* XXX paranoia check on length */
1066 sprintf(cmd_buf,"%s ",
1067 filexp(getval("VISUAL",getval("EDITOR",defeditor))));
1068 strcat(cmd_buf, filexp(fname));
1069 termdown(3);
1070 resetty(); /* make sure tty is friendly */
1071 r = doshell(sh,cmd_buf);/* invoke the shell */
1072 noecho(); /* and make terminal */
1073 crmode(); /* unfriendly again */
1074 return r;
1075 }
1076
1077 /* Consider a trn_pushdir, trn_popdir pair of functions */
1078