|
|
1.1 root 1: /* str_util.c */
2:
3: /* Synchronet string utility routines */
4:
5: /* $Id: str_util.c,v 1.35 2006/01/26 00:52:47 rswindell Exp $ */
6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
11: * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html *
12: * *
13: * This program is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU General Public License for more details: gpl.txt or *
18: * http://www.fsf.org/copyleft/gpl.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38: #include "sbbs.h"
39:
40: /****************************************************************************/
41: /* Removes ctrl-a codes from the string 'instr' */
42: /****************************************************************************/
43: char* DLLCALL remove_ctrl_a(char *instr, char *outstr)
44: {
45: char str[1024],*p;
46: uint i,j;
47:
48: for(i=j=0;instr[i] && j<sizeof(str)-1;i++) {
49: if(instr[i]==CTRL_A && instr[i+1]!=0)
50: i++;
51: else str[j++]=instr[i];
52: }
53: str[j]=0;
54: if(outstr!=NULL)
55: p=outstr;
56: else
57: p=instr;
58: strcpy(p,str);
59: return(p);
60: }
61:
62: char* DLLCALL strip_ctrl(char *str)
63: {
64: char tmp[1024];
65: int i,j;
66:
67: for(i=j=0;str[i] && j<(int)sizeof(tmp)-1;i++) {
68: if(str[i]==CTRL_A && str[i+1]!=0)
69: i++;
70: else if((uchar)str[i]>=' ')
71: tmp[j++]=str[i];
72: }
73: if(i!=j) {
74: tmp[j]=0;
75: strcpy(str,tmp);
76: }
77: return(str);
78: }
79:
80: char* DLLCALL strip_exascii(char *str)
81: {
82: char tmp[1024];
83: int i,j;
84:
85: for(i=j=0;str[i] && j<(int)sizeof(tmp)-1;i++)
86: if(!(str[i]&0x80))
87: tmp[j++]=str[i];
88: tmp[j]=0;
89: strcpy(str,tmp);
90: return(str);
91: }
92:
93: char* DLLCALL prep_file_desc(char *str)
94: {
95: char tmp[1024];
96: int i,j;
97:
98: for(i=j=0;str[i];i++)
99: if(str[i]==CTRL_A && str[i+1]!=0)
100: i++;
101: else if(j && str[i]<=' ' && tmp[j-1]==' ')
102: continue;
103: else if(i && !isalnum(str[i]) && str[i]==str[i-1])
104: continue;
105: else if((uchar)str[i]>=' ')
106: tmp[j++]=str[i];
107: else if(str[i]==TAB || (str[i]==CR && str[i+1]==LF))
108: tmp[j++]=' ';
109: tmp[j]=0;
110: strcpy(str,tmp);
111: return(str);
112: }
113:
114: /****************************************************************************/
115: /* Pattern matching string search of 'insearchof' in 'fname'. */
116: /****************************************************************************/
117: BOOL DLLCALL findstr(char* insearchof, char* fname)
118: {
119: char* p;
120: char str[128];
121: char search[81];
122: int c;
123: int i;
124: BOOL found;
125: FILE* stream;
126:
127: if((stream=fopen(fname,"r"))==NULL)
128: return(FALSE);
129:
130: SAFECOPY(search,insearchof);
131: strupr(search);
132:
133: found=FALSE;
134:
135: while(!feof(stream) && !ferror(stream) && !found) {
136: if(!fgets(str,sizeof(str),stream))
137: break;
138:
139: found=FALSE;
140:
141: p=str;
142: while(*p && *p<=' ') p++; /* Skip white-space */
143:
144: if(*p==';') /* comment */
145: continue;
146:
147: if(*p=='!') { /* !match */
148: found=TRUE;
149: p++;
150: }
151:
152: truncsp(p);
153: c=strlen(p);
154: if(c) {
155: c--;
156: strupr(p);
157: if(p[c]=='~') {
158: p[c]=0;
159: if(strstr(search,p))
160: found=!found;
161: }
162:
163: else if(p[c]=='^' || p[c]=='*') {
164: p[c]=0;
165: if(!strncmp(p,search,c))
166: found=!found;
167: }
168:
169: else if(p[0]=='*') {
170: i=strlen(search);
171: if(i<c)
172: continue;
173: if(!strncmp(p+1,search+(i-c),c))
174: found=!found;
175: }
176:
177: else if(!strcmp(p,search))
178: found=!found;
179: }
180: }
181: fclose(stream);
182: return(found);
183: }
184:
185: /****************************************************************************/
186: /* Searches the file <name>.can in the TEXT directory for matches */
187: /* Returns TRUE if found in list, FALSE if not. */
188: /****************************************************************************/
189: BOOL DLLCALL trashcan(scfg_t* cfg, char* insearchof, char* name)
190: {
191: char fname[MAX_PATH+1];
192:
193: sprintf(fname,"%s%s.can",cfg->text_dir,name);
194: return(findstr(insearchof,fname));
195: }
196:
197: /****************************************************************************/
198: /* Returns the number of characters in 'str' not counting ctrl-ax codes */
199: /* or the null terminator */
200: /****************************************************************************/
201: int bstrlen(char *str)
202: {
203: int i=0;
204:
205: while(*str) {
206: if(*str==CTRL_A)
207: str++;
208: else
209: i++;
210: if(!(*str)) break;
211: str++; }
212: return(i);
213: }
214:
215: /****************************************************************************/
216: /* Returns in 'string' a character representation of the number in l with */
217: /* commas. */
218: /****************************************************************************/
219: char* DLLCALL ultoac(ulong l, char *string)
220: {
221: char str[256];
222: int i,j,k;
223:
224: ultoa(l,str,10);
225: i=strlen(str)-1;
226: j=i/3+1+i;
227: string[j--]=0;
228: for(k=1;i>-1;k++) {
229: string[j--]=str[i--];
230: if(j>0 && !(k%3))
231: string[j--]=','; }
232: return(string);
233: }
234:
235: /****************************************************************************/
236: /* Truncate string at first occurance of char in specified character set */
237: /****************************************************************************/
238: char* DLLCALL truncstr(char* str, const char* set)
239: {
240: char* p;
241:
242: p=strpbrk(str,set);
243: if(p!=NULL && *p!=0)
244: *p=0;
245:
246: return(p);
247: }
248:
249: /****************************************************************************/
250: /* rot13 encoder/decoder - courtesy of Mike Acar */
251: /****************************************************************************/
252: char* DLLCALL rot13(char* str)
253: {
254: char ch, cap;
255: char* p;
256:
257: p=str;
258: while((ch=*p)!=0) {
259: cap = ch & 32;
260: ch &= ~cap;
261: ch = ((ch >= 'A') && (ch <= 'Z') ? ((ch - 'A' + 13) % 26 + 'A') : ch) | cap;
262: *(p++)=ch;
263: }
264:
265: return(str);
266: }
267:
268: /****************************************************************************/
269: /* Puts a backslash on path strings if not just a drive letter and colon */
270: /****************************************************************************/
271: void backslashcolon(char *str)
272: {
273: int i;
274:
275: i=strlen(str);
276: if(i && !IS_PATH_DELIM(str[i-1]) && str[i-1]!=':') {
277: str[i]=PATH_DELIM;
278: str[i+1]=0;
279: }
280: }
281:
282: /****************************************************************************/
283: /* Compares pointers to pointers to char. Used in conjuction with qsort() */
284: /****************************************************************************/
285: int pstrcmp(char **str1, char **str2)
286: {
287: return(strcmp(*str1,*str2));
288: }
289:
290: /****************************************************************************/
291: /* Returns the number of characters that are the same between str1 and str2 */
292: /****************************************************************************/
293: int strsame(char *str1, char *str2)
294: {
295: int i,j=0;
296:
297: for(i=0;str1[i];i++)
298: if(str1[i]==str2[i]) j++;
299: return(j);
300: }
301:
302:
303: /****************************************************************************/
304: /* Returns string for 2 digit hex+ numbers up to 575 */
305: /****************************************************************************/
306: char *hexplus(uint num, char *str)
307: {
308: sprintf(str,"%03x",num);
309: str[0]=num/0x100 ? 'f'+(num/0x10)-0xf : str[1];
310: str[1]=str[2];
311: str[2]=0;
312: return(str);
313: }
314:
315: /****************************************************************************/
316: /* Converts an ASCII Hex string into an ulong */
317: /* by Steve Deppe (Ille Homine Albe) */
318: /****************************************************************************/
319: ulong ahtoul(char *str)
320: {
321: ulong l,val=0;
322:
323: while((l=(*str++)|0x20)!=0x20)
324: val=(l&0xf)+(l>>6&1)*9+val*16;
325: return(val);
326: }
327:
328: /****************************************************************************/
329: /* Converts hex-plus string to integer */
330: /****************************************************************************/
331: uint hptoi(char *str)
332: {
333: char tmp[128];
334: uint i;
335:
336: if(!str[1] || toupper(str[0])<='F')
337: return(ahtoul(str));
338: strcpy(tmp,str);
339: tmp[0]='F';
340: i=ahtoul(tmp)+((toupper(str[0])-'F')*0x10);
341: return(i);
342: }
343:
344: /****************************************************************************/
345: /* Returns 1 if a is a valid ctrl-a code, 0 if it isn't. */
346: /****************************************************************************/
347: BOOL DLLCALL validattr(char a)
348: {
349:
350: switch(toupper(a)) {
351: case '+': /* push attr */
352: case '-': /* pop attr */
353: case '_': /* clear */
354: case 'B': /* blue fg */
355: case 'C': /* cyan fg */
356: case 'G': /* green fg */
357: case 'H': /* high fg */
358: case 'I': /* blink */
359: case 'K': /* black fg */
360: case 'L': /* cls */
361: case 'M': /* magenta fg */
362: case 'N': /* normal */
363: case 'P': /* pause */
364: case 'R': /* red fg */
365: case 'W': /* white fg */
366: case 'Y': /* yellow fg */
367: case '0': /* black bg */
368: case '1': /* red bg */
369: case '2': /* green bg */
370: case '3': /* brown bg */
371: case '4': /* blue bg */
372: case '5': /* magenta bg */
373: case '6': /* cyan bg */
374: case '7': /* white bg */
375: return(TRUE);
376: }
377: return(FALSE);
378: }
379:
380: /****************************************************************************/
381: /* Strips invalid Ctrl-Ax sequences from str */
382: /* Returns number of ^A's in line */
383: /****************************************************************************/
384: size_t DLLCALL strip_invalid_attr(char *strin)
385: {
386: char str[1024];
387: size_t a,c,d;
388:
389: for(a=c=d=0;strin[c] && d<sizeof(str)-1;c++) {
390: if(strin[c]==CTRL_A && strin[c+1]!=0) {
391: a++;
392: if(!validattr(strin[c+1])) {
393: c++;
394: continue;
395: }
396: }
397: str[d++]=strin[c];
398: }
399: str[d]=0;
400: strcpy(strin,str);
401: return(a);
402: }
403:
404: char* replace_named_values(const char* src
405: ,char* buf
406: ,size_t buflen /* includes '\0' terminator */
407: ,char* escape_seq
408: ,named_string_t* string_list
409: ,named_int_t* int_list
410: ,BOOL case_sensitive)
411: {
412: char val[32];
413: size_t i;
414: size_t esc_len=0;
415: size_t name_len;
416: size_t value_len;
417: char* p = buf;
418: int (*cmp)(const char*, const char*, size_t);
419:
420: if(case_sensitive)
421: cmp=strncmp;
422: else
423: cmp=strnicmp;
424:
425: if(escape_seq!=NULL)
426: esc_len = strlen(escape_seq);
427:
428: while(*src && (size_t)(p-buf) < buflen-1) {
429: if(esc_len) {
430: if(cmp(src, escape_seq, esc_len)!=0) {
431: *p++ = *src++;
432: continue;
433: }
434: src += esc_len; /* skip the escape seq */
435: }
436: if(string_list) {
437: for(i=0; string_list[i].name!=NULL /* terminator */; i++) {
438: name_len = strlen(string_list[i].name);
439: if(cmp(src, string_list[i].name, name_len)==0) {
440: value_len = strlen(string_list[i].value);
441: if((p-buf)+value_len > buflen-1) /* buffer overflow? */
442: value_len = (buflen-1)-(p-buf); /* truncate value */
443: memcpy(p, string_list[i].value, value_len);
444: p += value_len;
445: src += name_len;
446: break;
447: }
448: }
449: if(string_list[i].name!=NULL) /* variable match */
450: continue;
451: }
452: if(int_list) {
453: for(i=0; int_list[i].name!=NULL /* terminator */; i++) {
454: name_len = strlen(int_list[i].name);
455: if(cmp(src, int_list[i].name, name_len)==0) {
456: SAFEPRINTF(val,"%d",int_list[i].value);
457: value_len = strlen(val);
458: if((p-buf)+value_len > buflen-1) /* buffer overflow? */
459: value_len = (buflen-1)-(p-buf); /* truncate value */
460: memcpy(p, val, value_len);
461: p += value_len;
462: src += name_len;
463: break;
464: }
465: }
466: if(int_list[i].name!=NULL) /* variable match */
467: continue;
468: }
469:
470: *p++ = *src++;
471: }
472: *p=0; /* terminate string in destination buffer */
473:
474: return(buf);
475: }
476:
477: char* replace_keyed_values(const char* src
478: ,char* buf
479: ,size_t buflen /* includes '\0' terminator */
480: ,char esc_char
481: ,keyed_string_t* string_list
482: ,keyed_int_t* int_list
483: ,BOOL case_sensitive)
484: {
485: char val[32];
486: size_t i;
487: size_t value_len;
488: char* p = buf;
489:
490:
491: while(*src && (size_t)(p-buf) < buflen-1) {
492: if(esc_char) {
493: if(*src != esc_char) {
494: *p++ = *src++;
495: continue;
496: }
497: src ++; /* skip the escape char */
498: }
499: if(string_list) {
500: for(i=0; string_list[i].key!=0 /* terminator */; i++) {
501: if((case_sensitive && *src == string_list[i].key)
502: || ((!case_sensitive) && toupper(*src) == toupper(string_list[i].key))) {
503: value_len = strlen(string_list[i].value);
504: if((p-buf)+value_len > buflen-1) /* buffer overflow? */
505: value_len = (buflen-1)-(p-buf); /* truncate value */
506: memcpy(p, string_list[i].value, value_len);
507: p += value_len;
508: src++;
509: break;
510: }
511: }
512: if(string_list[i].key!=0) /* variable match */
513: continue;
514: }
515: if(int_list) {
516: for(i=0; int_list[i].key!=0 /* terminator */; i++) {
517: if((case_sensitive && *src == int_list[i].key)
518: || ((!case_sensitive) && toupper(*src) == toupper(int_list[i].key))) {
519: SAFEPRINTF(val,"%d",int_list[i].value);
520: value_len = strlen(val);
521: if((p-buf)+value_len > buflen-1) /* buffer overflow? */
522: value_len = (buflen-1)-(p-buf); /* truncate value */
523: memcpy(p, val, value_len);
524: p += value_len;
525: src++;
526: break;
527: }
528: }
529: if(int_list[i].key!=0) /* variable match */
530: continue;
531: }
532:
533: *p++ = *src++;
534: }
535: *p=0; /* terminate string in destination buffer */
536:
537: return(buf);
538: }
539:
540:
541: #if 0 /* replace_*_values test */
542:
543: void main(void)
544: {
545: char buf[128];
546: keyed_string_t strs[] = {
547: { '+', "plus" },
548: { '=', "equals" },
549: { 0 }
550: };
551: keyed_int_t ints[] = {
552: { 'o', 1 },
553: { 't', 2 },
554: { 'h', 3 },
555: { NULL }
556: };
557:
558: printf("'%s'\n", replace_keyed_values("$o $+ $t $= $h", buf, sizeof(buf), '$'
559: ,strs, ints, FALSE));
560:
561: }
562:
563: #endif
564:
565: #if 0 /* to be moved here from xtrn.cpp */
566:
567: char* quoted_string(const char* str, char* buf, size_t maxlen)
568: {
569: if(strchr(str,' ')==NULL)
570: return((char*)str);
571: safe_snprintf(buf,maxlen,"\"%s\"",str);
572: return(buf);
573: }
574:
575: #endif
576:
577: #if 0 /* I think is is a misguided idea :-( */
578:
579: char* sbbs_cmdstr(const char* src
580: ,char* buf
581: ,size_t buflen /* includes '\0' terminator */
582: ,scfg_t* scfg
583: ,user_t* user
584: ,int node_num
585: ,int minutes
586: ,int rows
587: ,int timeleft
588: ,SOCKET socket_descriptor
589: ,char* protocol
590: ,char* ip_address
591: ,char* fpath
592: ,char* fspec
593: )
594: {
595: const char* nulstr = "";
596: char alias_buf[LEN_ALIAS+1];
597: char fpath_buf[MAX_PATH+1];
598: char fspec_buf[MAX_PATH+1];
599: char sysop_buf[sizeof(scfg->sys_op)];
600:
601: keyed_string_t str_list[] = {
602: /* user alias */
603: { 'a', user!=NULL ? quoted_string(user->alias, alias_buf, sizeof(alias_buf)) : nulstr },
604: { 'A', user!=NULL ? user->alias : nulstr },
605:
606: /* connection */
607: { 'c', protocol },
608: { 'C', protocol },
609:
610: /* file path */
611: { 'f', quoted_string(fpath, fpath_buf, sizeof(fpath_buf)) },
612: { 'F', fpath },
613:
614: /* temp dir */
615: { 'g', scfg->temp_dir },
616: { 'G', scfg->temp_dir },
617:
618: /* IP address */
619: { 'h', ip_address },
620: { 'H', ip_address },
621:
622: /* data dir */
623: { 'j', scfg->data_dir },
624: { 'J', scfg->data_dir },
625:
626: /* ctrl dir */
627: { 'k', scfg->ctrl_dir },
628: { 'K', scfg->ctrl_dir },
629:
630: /* node dir */
631: { 'n', scfg->node_dir },
632: { 'N', scfg->node_dir },
633:
634: /* sysop */
635: { 'o', quoted_string(scfg->sys_op, sysop_buf, sizeof(sysop_buf)) },
636: { 'O', scfg->sys_op },
637:
638: /* protocol */
639: { 'p', protocol },
640: { 'P', protocol },
641:
642: /* system QWK-ID */
643: { 'q', scfg->sys_id },
644: { 'Q', scfg->sys_id },
645:
646: /* file spec */
647: { 's', quoted_string(fspec, fspec_buf, sizeof(fspec_buf)) },
648: { 'S', fspec },
649:
650: /* UART I/O Address (in hex) 'f' for FOSSIL */
651: { 'u', "f" },
652: { 'U', "f" },
653:
654: /* text dir */
655: { 'z', scfg->text_dir },
656: { 'Z', scfg->text_dir },
657:
658: /* exec dir */
659: { '!', scfg->exec_dir },
660: { '@',
661: #ifndef __unix__
662: scfg->exec_dir
663: #else
664: nulstr
665: #endif
666: },
667:
668: /* .exe (on Windows) */
669: { '.',
670: #ifndef __unix__
671: ".exe"
672: #else
673: nulstr
674: #endif
675: },
676:
677: /* terminator */
678: { 0 }
679: };
680: keyed_int_t int_list[] = {
681: /* node number */
682: { '#', node_num },
683:
684: /* DTE rate */
685: { 'b', 38400 },
686: { 'B', 38400 },
687:
688: /* DCE rate */
689: { 'd', 30000 },
690: { 'D', 30000 },
691:
692: /* Estimated Rate (cps) */
693: { 'e', 3000 },
694: { 'E', 3000 },
695:
696: { 'h', socket_descriptor },
697: { 'H', socket_descriptor },
698:
699: { 'l', user==NULL ? 0 : scfg->level_linespermsg[user->level] },
700: { 'L', user==NULL ? 0 : scfg->level_linespermsg[user->level] },
701:
702: { 'm', minutes },
703: { 'M', minutes },
704:
705: { 'r', rows },
706: { 'R', rows },
707:
708: /* Time left in seconds */
709: { 't', timeleft },
710: { 'T', timeleft },
711:
712: /* Credits */
713: { '$', user==NULL ? 0 : (user->cdt+user->freecdt) },
714:
715: /* terminator */
716: { 0 }
717: };
718:
719:
720: return replace_keyed_values(src, buf, buflen, '%', str_list, int_list, TRUE);
721: }
722:
723: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.