|
|
1.1 ! root 1: /* $Id: wordwrap.c,v 1.6 2010/05/26 04:54:37 rswindell Exp $ */ ! 2: ! 3: /**************************************************************************** ! 4: * @format.tab-size 4 (Plain Text/Source Code File Header) * ! 5: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * ! 6: * * ! 7: * Copyright 2010 Rob Swindell - http://www.synchro.net/copyright.html * ! 8: * * ! 9: * This program is free software; you can redistribute it and/or * ! 10: * modify it under the terms of the GNU General Public License * ! 11: * as published by the Free Software Foundation; either version 2 * ! 12: * of the License, or (at your option) any later version. * ! 13: * See the GNU General Public License for more details: gpl.txt or * ! 14: * http://www.fsf.org/copyleft/gpl.html * ! 15: * * ! 16: * Anonymous FTP access to the most recent released source is available at * ! 17: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * ! 18: * * ! 19: * Anonymous CVS access to the development source and modification history * ! 20: * is available at cvs.synchro.net:/cvsroot/sbbs, example: * ! 21: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login * ! 22: * (just hit return, no password is necessary) * ! 23: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src * ! 24: * * ! 25: * For Synchronet coding style and modification guidelines, see * ! 26: * http://www.synchro.net/source.html * ! 27: * * ! 28: * You are encouraged to submit any modifications (preferably in Unix diff * ! 29: * format) via e-mail to [email protected] * ! 30: * * ! 31: * Note: If this box doesn't appear square, then you need to fix your tabs. * ! 32: ****************************************************************************/ ! 33: ! 34: #include <genwrap.h> ! 35: #include <stdlib.h> /* realloc */ ! 36: #include "wordwrap.h" ! 37: ! 38: static int get_prefix(const char *text, int *bytes, int *len, int maxlen) ! 39: { ! 40: int tmp_prefix_bytes,tmp_prefix_len; ! 41: int expect; ! 42: int depth; ! 43: ! 44: *bytes=0; ! 45: *len=0; ! 46: tmp_prefix_bytes=0; ! 47: tmp_prefix_len=0; ! 48: depth=0; ! 49: expect=1; ! 50: if(text[0]!=' ') ! 51: expect=2; ! 52: while(expect) { ! 53: tmp_prefix_bytes++; ! 54: /* Skip CTRL-A codes */ ! 55: while(text[tmp_prefix_bytes-1]=='\x01') { ! 56: tmp_prefix_bytes++; ! 57: if(text[tmp_prefix_bytes-1]=='\x01') ! 58: break; ! 59: tmp_prefix_bytes++; ! 60: } ! 61: tmp_prefix_len++; ! 62: if(text[tmp_prefix_bytes-1]==0 || text[tmp_prefix_bytes-1]=='\n' || text[tmp_prefix_bytes-1]=='\r') ! 63: break; ! 64: switch(expect) { ! 65: case 1: /* At start of possible quote (Next char should be space) */ ! 66: if(text[tmp_prefix_bytes-1]!=' ') ! 67: expect=0; ! 68: else ! 69: expect++; ! 70: break; ! 71: case 2: /* At start of nick (next char should be alphanum or '>') */ ! 72: case 3: /* At second nick initial (next char should be alphanum or '>') */ ! 73: case 4: /* At third nick initial (next char should be alphanum or '>') */ ! 74: if(text[tmp_prefix_bytes-1]==' ' || text[tmp_prefix_bytes-1]==0) ! 75: expect=0; ! 76: else ! 77: if(text[tmp_prefix_bytes-1]=='>') ! 78: expect=6; ! 79: else ! 80: expect++; ! 81: break; ! 82: case 5: /* After three regular chars, next HAS to be a '>') */ ! 83: if(text[tmp_prefix_bytes-1]!='>') ! 84: expect=0; ! 85: else ! 86: expect++; ! 87: break; ! 88: case 6: /* At '>' next char must be a space */ ! 89: if(text[tmp_prefix_bytes-1]!=' ') ! 90: expect=0; ! 91: else { ! 92: expect=1; ! 93: *len=tmp_prefix_len; ! 94: *bytes=tmp_prefix_bytes; ! 95: depth++; ! 96: /* Some editors don't put double spaces in between */ ! 97: if(text[tmp_prefix_bytes]!=' ') ! 98: expect++; ! 99: } ! 100: break; ! 101: default: ! 102: expect=0; ! 103: break; ! 104: } ! 105: } ! 106: if(*bytes >= maxlen) { ! 107: // lprintf(LOG_CRIT, "Prefix bytes %u is larger than buffer (%u) here: %*.*s",*bytes,maxlen,maxlen,maxlen,text); ! 108: *bytes=maxlen-1; ! 109: } ! 110: return(depth); ! 111: } ! 112: ! 113: static void outbuf_append(char **outbuf, char **outp, char *append, int len, int *outlen) ! 114: { ! 115: char *p; ! 116: ! 117: /* Terminate outbuf */ ! 118: **outp=0; ! 119: /* Check if there's room */ ! 120: if(*outp - *outbuf + len < *outlen) { ! 121: memcpy(*outp, append, len); ! 122: *outp+=len; ! 123: return; ! 124: } ! 125: /* Not enough room, double the size. */ ! 126: *outlen *= 2; ! 127: p=realloc(*outbuf, *outlen); ! 128: if(p==NULL) { ! 129: /* Can't do it. */ ! 130: *outlen/=2; ! 131: return; ! 132: } ! 133: /* Set outp for new buffer */ ! 134: *outp=p+(*outp - *outbuf); ! 135: *outbuf=p; ! 136: memcpy(*outp, append, len); ! 137: *outp+=len; ! 138: return; ! 139: } ! 140: ! 141: static int compare_prefix(char *old_prefix, int old_prefix_bytes, const char *new_prefix, int new_prefix_bytes) ! 142: { ! 143: int i; ! 144: ! 145: if(new_prefix_bytes != old_prefix_bytes) { ! 146: if(new_prefix_bytes < old_prefix_bytes) { ! 147: if(memcmp(old_prefix, new_prefix, new_prefix_bytes)!=0) ! 148: return(-1); ! 149: for(i=new_prefix_bytes; i<old_prefix_bytes; i++) { ! 150: if(!isspace((unsigned char)old_prefix[i])) ! 151: return(-1); ! 152: } ! 153: } ! 154: else { ! 155: if(memcmp(old_prefix, new_prefix, old_prefix_bytes)!=0) ! 156: return(-1); ! 157: for(i=old_prefix_bytes; i<new_prefix_bytes; i++) { ! 158: if(!isspace((unsigned char)new_prefix[i])) ! 159: return(-1); ! 160: } ! 161: } ! 162: return(0); ! 163: } ! 164: if(memcmp(old_prefix,new_prefix,new_prefix_bytes)!=0) ! 165: return(-1); ! 166: ! 167: return(0); ! 168: } ! 169: ! 170: char* wordwrap(char* inbuf, int len, int oldlen, BOOL handle_quotes) ! 171: { ! 172: int l; ! 173: int crcount=0; ! 174: long i,k,t; ! 175: int ocol=1; ! 176: int icol=1; ! 177: char* outbuf; ! 178: char* outp; ! 179: char* linebuf; ! 180: char* prefix=NULL; ! 181: int prefix_len=0; ! 182: int prefix_bytes=0; ! 183: int quote_count=0; ! 184: int old_prefix_bytes=0; ! 185: int outbuf_size=0; ! 186: int inbuf_len=strlen(inbuf); ! 187: ! 188: outbuf_size=inbuf_len*3+1; ! 189: if((outbuf=(char*)malloc(outbuf_size))==NULL) ! 190: return NULL; ! 191: outp=outbuf; ! 192: ! 193: if((linebuf=(char*)malloc(inbuf_len+2))==NULL) /* room for ^A codes */ ! 194: return NULL; ! 195: ! 196: if(handle_quotes) { ! 197: if((prefix=(char *)malloc(inbuf_len+1))==NULL) { /* room for ^A codes */ ! 198: free(linebuf); ! 199: return NULL; ! 200: } ! 201: prefix[0]=0; ! 202: } ! 203: ! 204: outbuf[0]=0; ! 205: /* Get prefix from the first line (ouch) */ ! 206: l=0; ! 207: i=0; ! 208: if(handle_quotes && (quote_count=get_prefix(inbuf, &prefix_bytes, &prefix_len, len*2+2))!=0) { ! 209: i+=prefix_bytes; ! 210: if(prefix_len>len/3*2) { ! 211: /* This prefix is insane (more than 2/3rds of the new width) hack it down to size */ ! 212: /* Since we're hacking it, we will always end up with a hardcr on this line. */ ! 213: /* ToDo: Something prettier would be nice. */ ! 214: sprintf(prefix," %d> ",quote_count); ! 215: prefix_len=strlen(prefix); ! 216: prefix_bytes=strlen(prefix); ! 217: } ! 218: else { ! 219: memcpy(prefix,inbuf,prefix_bytes); ! 220: /* Terminate prefix */ ! 221: prefix[prefix_bytes]=0; ! 222: } ! 223: memcpy(linebuf,prefix,prefix_bytes); ! 224: l=prefix_bytes; ! 225: ocol=prefix_len+1; ! 226: icol=prefix_len+1; ! 227: old_prefix_bytes=prefix_bytes; ! 228: } ! 229: for(; inbuf[i]; i++) { ! 230: if(l>=len*2+2) { ! 231: l-=4; ! 232: linebuf[l]=0; ! 233: // lprintf(LOG_CRIT, "Word wrap line buffer exceeded... munging line %s",linebuf); ! 234: } ! 235: switch(inbuf[i]) { ! 236: case '\r': ! 237: crcount++; ! 238: break; ! 239: case '\n': ! 240: if(handle_quotes && (quote_count=get_prefix(inbuf+i+1, &prefix_bytes, &prefix_len, len*2+2))!=0) { ! 241: /* Move the input pointer offset to the last char of the prefix */ ! 242: i+=prefix_bytes; ! 243: } ! 244: if(!inbuf[i+1]) { /* EOF */ ! 245: linebuf[l++]='\r'; ! 246: linebuf[l++]='\n'; ! 247: outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size); ! 248: l=0; ! 249: ocol=1; ! 250: } ! 251: /* If there's a new prefix, it is a hardcr */ ! 252: else if(compare_prefix(prefix, old_prefix_bytes, inbuf+i+1-prefix_bytes, prefix_bytes)!=0) { ! 253: if(prefix_len>len/3*2) { ! 254: /* This prefix is insane (more than 2/3rds of the new width) hack it down to size */ ! 255: /* Since we're hacking it, we will always end up with a hardcr on this line. */ ! 256: /* ToDo: Something prettier would be nice. */ ! 257: sprintf(prefix," %d> ",quote_count); ! 258: prefix_len=strlen(prefix); ! 259: prefix_bytes=strlen(prefix); ! 260: } ! 261: else { ! 262: memcpy(prefix,inbuf+i+1-prefix_bytes,prefix_bytes); ! 263: /* Terminate prefix */ ! 264: prefix[prefix_bytes]=0; ! 265: } ! 266: linebuf[l++]='\r'; ! 267: linebuf[l++]='\n'; ! 268: outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size); ! 269: memcpy(linebuf,prefix,prefix_bytes); ! 270: l=prefix_bytes; ! 271: ocol=prefix_len+1; ! 272: old_prefix_bytes=prefix_bytes; ! 273: } ! 274: else if(isspace((unsigned char)inbuf[i+1]) && inbuf[i+1] != '\n' && inbuf[i+1] != '\r') { /* Next line starts with whitespace. This is a "hard" CR. */ ! 275: linebuf[l++]='\r'; ! 276: linebuf[l++]='\n'; ! 277: outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size); ! 278: l=prefix_bytes; ! 279: ocol=prefix_len+1; ! 280: } ! 281: else { ! 282: if(icol < oldlen) { /* If this line is overly long, It's impossible for the next word to fit */ ! 283: /* k will equal the length of the first word on the next line */ ! 284: for(k=0; inbuf[i+1+k] && (!isspace((unsigned char)inbuf[i+1+k])); k++); ! 285: if(icol+k+1 < oldlen) { /* The next word would have fit but isn't here. Must be a hard CR */ ! 286: linebuf[l++]='\r'; ! 287: linebuf[l++]='\n'; ! 288: outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size); ! 289: if(prefix) ! 290: memcpy(linebuf,prefix,prefix_bytes); ! 291: l=prefix_bytes; ! 292: ocol=prefix_len+1; ! 293: } ! 294: else { /* Not a hard CR... add space if needed */ ! 295: if(l<1 || !isspace((unsigned char)linebuf[l-1])) { ! 296: linebuf[l++]=' '; ! 297: ocol++; ! 298: } ! 299: } ! 300: } ! 301: else { /* Not a hard CR... add space if needed */ ! 302: if(l<1 || !isspace((unsigned char)linebuf[l-1])) { ! 303: linebuf[l++]=' '; ! 304: ocol++; ! 305: } ! 306: } ! 307: } ! 308: icol=prefix_len+1; ! 309: break; ! 310: case '\x1f': /* Delete... meaningless... strip. */ ! 311: break; ! 312: case '\b': /* Backspace... handle if possible, but don't go crazy. */ ! 313: if(l>0) { ! 314: if(l>1 && linebuf[l-2]=='\x01') { ! 315: if(linebuf[l-1]=='\x01') { ! 316: ocol--; ! 317: icol--; ! 318: } ! 319: l-=2; ! 320: } ! 321: else { ! 322: l--; ! 323: ocol--; ! 324: icol--; ! 325: } ! 326: } ! 327: break; ! 328: case '\t': /* TAB */ ! 329: linebuf[l++]=inbuf[i]; ! 330: /* Can't ever wrap on whitespace remember. */ ! 331: icol++; ! 332: ocol++; ! 333: while(ocol%8) ! 334: ocol++; ! 335: while(icol%8) ! 336: icol++; ! 337: break; ! 338: case '\x01': /* CTRL-A */ ! 339: linebuf[l++]=inbuf[i++]; ! 340: if(inbuf[i]!='\x01') { ! 341: linebuf[l++]=inbuf[i]; ! 342: break; ! 343: } ! 344: default: ! 345: linebuf[l++]=inbuf[i]; ! 346: ocol++; ! 347: icol++; ! 348: if(ocol>len && !isspace((unsigned char)inbuf[i])) { /* Need to wrap here */ ! 349: /* Find the start of the last word */ ! 350: k=l; /* Original next char */ ! 351: l--; /* Move back to the last char */ ! 352: while((!isspace((unsigned char)linebuf[l])) && l>0) /* Move back to the last non-space char */ ! 353: l--; ! 354: if(l==0) { /* Couldn't wrap... must chop. */ ! 355: l=k; ! 356: while(l>1 && linebuf[l-2]=='\x01' && linebuf[l-1]!='\x01') ! 357: l-=2; ! 358: if(l>0 && linebuf[l-1]=='\x01') ! 359: l--; ! 360: if(l>0) ! 361: l--; ! 362: } ! 363: t=l+1; /* Store start position of next line */ ! 364: /* Move to start of whitespace */ ! 365: while(l>0 && isspace((unsigned char)linebuf[l])) ! 366: l--; ! 367: outbuf_append(&outbuf, &outp, linebuf, l+1, &outbuf_size); ! 368: outbuf_append(&outbuf, &outp, "\r\n", 2, &outbuf_size); ! 369: /* Move trailing words to start of buffer. */ ! 370: l=prefix_bytes; ! 371: if(k-t>0) /* k-1 is the last char position. t is the start of the next line position */ ! 372: memmove(linebuf+l, linebuf+t, k-t); ! 373: l+=k-t; ! 374: /* Find new ocol */ ! 375: for(ocol=prefix_len+1,t=prefix_bytes; t<l; t++) { ! 376: switch(linebuf[t]) { ! 377: case '\x01': /* CTRL-A */ ! 378: t++; ! 379: if(linebuf[t]!='\x01') ! 380: break; ! 381: /* Fall-through */ ! 382: default: ! 383: ocol++; ! 384: } ! 385: } ! 386: } ! 387: } ! 388: } ! 389: /* Trailing bits. */ ! 390: if(l) { ! 391: linebuf[l++]='\r'; ! 392: linebuf[l++]='\n'; ! 393: outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size); ! 394: } ! 395: *outp=0; ! 396: /* If there were no CRs in the input, strip all CRs */ ! 397: if(!crcount) { ! 398: for(inbuf=outbuf; *inbuf; inbuf++) { ! 399: if(*inbuf=='\r') ! 400: memmove(inbuf, inbuf+1, strlen(inbuf)); ! 401: } ! 402: } ! 403: free(linebuf); ! 404: ! 405: if(prefix) ! 406: free(prefix); ! 407: ! 408: return outbuf; ! 409: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.