|
|
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.