col.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. /* col.c
  2. * col [ -bfx ]
  3. */
  4. #include <stdio.h>
  5. #include <ctype.h>
  6. #define ESC '\033'
  7. /* second chars of escape codes */
  8. #define HALFDOWN '9'
  9. #define HALFUP '8'
  10. #define WHOLEUP '7'
  11. #define VT '\013' /* alternate form of full reverse lf */
  12. #define SO '\017' /* turn on alternate char set */
  13. #define SI '\016' /* turn it off */
  14. #define CHUNKSIZE 80
  15. /* globals */
  16. int backspace, /* can terminal backspace? */
  17. half, /* ok to do 1/2 forward lf? */
  18. squeeze, /* turn white-space into tabs */
  19. col, /* current column */
  20. whichalf, /* are we on full line or between lines? */
  21. backs, /* # of backspaces on current line */
  22. nextchunk, /* next time we need to alloc space for current text */
  23. altset; /* alternate character set mode */
  24. struct line /* double linked list */
  25. {
  26. char *text;
  27. struct line *up;
  28. struct line *down;
  29. int updist; /* one=1/2 lf, two=full lf */
  30. int downdist;
  31. } *root,*linit(),*scan();
  32. char *forward(),*strinsert();
  33. main(argc,argv)
  34. int argc;
  35. char **argv;
  36. {
  37. int x;
  38. char c,*argtmp;
  39. backspace=squeeze=1;
  40. whichalf=col=half=altset=backs=0;
  41. /* check args */
  42. if(argc>=2)
  43. while(argtmp = *++argv)
  44. if(**argv=='-')
  45. while(c = *++*argv)
  46. switch(c)
  47. {
  48. case 'b':
  49. backspace=0;
  50. break;
  51. case 'f':
  52. half=1;
  53. break;
  54. case 'x':
  55. squeeze=0;
  56. break;
  57. default:
  58. fprintf(stderr,"col: Bad option %c\n"
  59. ,c);
  60. exit(1);
  61. }
  62. else
  63. {
  64. fprintf(stderr,"col: Bad arg %s\n",argtmp);
  65. exit(1);
  66. }
  67. root=scan(NULL,NULL,NULL); /* All nulls because so far there are
  68. * no lines */
  69. printup(root); /* print all lines above + including root */
  70. if(root)
  71. printdown(root->down); /* print all lines below root */
  72. }
  73. struct line *
  74. scan(line,above,below)
  75. register struct line *line,*above,*below;
  76. {
  77. int leg,coltmp,eol;
  78. char *charalloc(),*rightins();
  79. register char c;
  80. col -= backs*2; /* get actual cursor pos on screen */
  81. backs=0;
  82. if(!line)
  83. {
  84. line=linit();
  85. line->text=charalloc(NULL,CHUNKSIZE);
  86. line->text[0]=NULL;
  87. }
  88. nextchunk=strlen(line->text)%CHUNKSIZE+CHUNKSIZE;
  89. coltmp=col;
  90. col=eol=0;
  91. line->text=forward(line->text,coltmp);
  92. while(!eol)
  93. switch(c=getchar())
  94. {
  95. case EOF:
  96. if(above)
  97. {
  98. above->down=line;
  99. line->up=above;
  100. }
  101. eol=1;
  102. break;
  103. case ESC:
  104. c=getchar(); /* find out what esc sequence is */
  105. switch(c)
  106. {
  107. case HALFDOWN:
  108. halfdown(line,above,below);
  109. eol=1;
  110. break;
  111. case HALFUP:
  112. halfup(line,above,below);
  113. eol=1;
  114. break;
  115. case WHOLEUP:
  116. wholeup(line,above,below);
  117. eol=1;
  118. break;
  119. }
  120. break;
  121. case '\r':
  122. col=backs=0;
  123. break;
  124. case '\n':
  125. wholedown(line,above,below);
  126. break;
  127. case VT:
  128. wholeup(line,above,below);
  129. break;
  130. case '\t':
  131. /* convert to spaces */
  132. do
  133. line->text=rightins(line->text,' ');
  134. while((col-backs*2)%8);
  135. break;
  136. case '\b':
  137. if(col)
  138. {
  139. col--;
  140. while(line->text[col]=='\b')
  141. col=(col<2) ? 0:col-2;
  142. }
  143. break;
  144. case SO:
  145. altset=1;
  146. break;
  147. case SI:
  148. altset=0;
  149. break;
  150. default:
  151. if(isprint(c))
  152. line->text=rightins(line->text,c);
  153. break;
  154. }
  155. return(line);
  156. }
  157. halfdown(line,above,below)
  158. register struct line *line,*above,*below;
  159. {
  160. whichalf=!whichalf;
  161. if(above)
  162. {
  163. line->updist=above->downdist;
  164. line->up=above;
  165. above->down=line;
  166. }
  167. if(half)
  168. {
  169. line->downdist=1;
  170. if(below)
  171. {
  172. below->up=line;
  173. if(below->updist==2)
  174. {
  175. below->updist=1;
  176. scan(NULL,line,below);
  177. }
  178. else /* below->updist==1 */
  179. scan(line->down=below,line,
  180. below->down);
  181. }
  182. else
  183. scan(NULL,line,NULL);
  184. }
  185. else
  186. {
  187. if(below)
  188. {
  189. below->up=line;
  190. line->down=below;
  191. if(whichalf)
  192. scan(below,line,below->down);
  193. else
  194. scan(line,above,below);
  195. }
  196. else
  197. {
  198. if(whichalf)
  199. scan(NULL,line,NULL);
  200. else
  201. scan(line,above,NULL);
  202. }
  203. }
  204. }
  205. halfup(line,above,below) /* bascially the same as HALFDOWN */
  206. register struct line *line,*above,*below;
  207. {
  208. whichalf=!whichalf;
  209. if(below)
  210. {
  211. line->downdist=below->updist;
  212. line->down=below;
  213. below->up=line;
  214. }
  215. if(half)
  216. {
  217. line->updist=1;
  218. if(above)
  219. {
  220. above->down=line;
  221. if(above->downdist==2)
  222. {
  223. above->downdist=1;
  224. scan(NULL,above,line);
  225. }
  226. else /* above->downdist==1 */
  227. scan(line->up=above,above->up,line);
  228. }
  229. else
  230. scan(NULL,NULL,line);
  231. }
  232. else
  233. {
  234. if(above)
  235. {
  236. above->down=line;
  237. line->up=above;
  238. if(whichalf)
  239. scan(line,above,below);
  240. else
  241. scan(above,above->up,line);
  242. }
  243. else
  244. {
  245. if(whichalf)
  246. scan(line,NULL,below);
  247. else
  248. scan(NULL,NULL,line);
  249. }
  250. }
  251. }
  252. wholedown(line,above,below)
  253. register struct line *line,*above,*below;
  254. {
  255. col=0; /* go to beginning of line */
  256. if(above)
  257. {
  258. line->updist=above->downdist;
  259. line->up=above;
  260. above->down=line;
  261. }
  262. if(half)
  263. {
  264. if(below)
  265. {
  266. line->down=below;
  267. line->downdist=below->updist;
  268. below->up=line;
  269. if(line->downdist==2)
  270. scan(below,line,below->down);
  271. else /* line->downdist==1 */
  272. {
  273. switch(below->downdist) /* next below*/
  274. {
  275. case 0: /* nothing down there */
  276. below->downdist=1;
  277. scan(NULL,below,NULL);
  278. break;
  279. case 1: /* 1/2 line, exactly what we
  280. * need */
  281. scan(below->down,below,
  282. below->down->down);
  283. break;
  284. case 2: /* put line between below and
  285. * below->down */
  286. below->downdist=1;
  287. below->down->updist=1;
  288. scan(NULL,below,below->down);
  289. break;
  290. }
  291. }
  292. }
  293. else /* create line 2 halves down from 'line' */
  294. {
  295. line->downdist=2;
  296. scan(NULL,line,NULL);
  297. }
  298. }
  299. else
  300. {
  301. if(below)
  302. {
  303. line->down=below;
  304. below->up=line;
  305. scan(below,line,below->down);
  306. }
  307. else
  308. scan(NULL,line,NULL);
  309. }
  310. }
  311. wholeup(line,above,below) /* bascially same as WHOLEDOWN */
  312. register struct line *line,*above,*below;
  313. {
  314. if(below)
  315. {
  316. line->downdist=below->updist;
  317. line->down=below;
  318. below->up=line;
  319. }
  320. if(half)
  321. {
  322. if(above)
  323. {
  324. line->up=above;
  325. line->updist=above->downdist;
  326. above->down=line;
  327. if(line->updist==2)
  328. scan(above,above->up,line);
  329. else /* line->updist==1 */
  330. switch(above->updist) /* next above */
  331. {
  332. case 0: /* nothing up there */
  333. above->updist=1;
  334. scan(NULL,NULL,above);
  335. break;
  336. case 1: /* 1/2 line, perfect */
  337. scan(above->up,above->up->up,
  338. above);
  339. break;
  340. case 2: /* squeeze a line between
  341. * above->up and above */
  342. above->updist=1;
  343. above->up->downdist=1;
  344. scan(NULL,above->up,above);
  345. break;
  346. }
  347. }
  348. else /* create line 2 halves up from 'line' */
  349. {
  350. line->updist=2;
  351. scan(NULL,NULL,line);
  352. }
  353. }
  354. else
  355. {
  356. if(above)
  357. {
  358. line->up=above;
  359. above->down=line;
  360. scan(above,above->up,line);
  361. }
  362. else
  363. scan(NULL,NULL,line);
  364. }
  365. }
  366. char *
  367. rightins(s,c) /* choose the right way to insert c into s[col] */
  368. register char *s,c;
  369. {
  370. c=altset ? c|0200 : c&0177; /* use high bit as flag to tell whether or
  371. * not char is in alt character set */
  372. if(!s[col])
  373. {
  374. if(col+2>=nextchunk)
  375. s=charalloc(s,nextchunk+=CHUNKSIZE);
  376. s[col++]=c;
  377. s[col]=NULL;
  378. return(s);
  379. }
  380. else
  381. {
  382. if(s[col]==' ' || !backspace) /* overwrite it */
  383. s[col]=c;
  384. else if(c!=' ') /* no use inserting a space */
  385. {
  386. ++col;
  387. s=strinsert(s,col++,'\b');
  388. s=strinsert(s,col,c);
  389. backs++;
  390. }
  391. return(forward(s,1));
  392. }
  393. }
  394. struct line *
  395. linit() /* alloc line structure and nullify all members */
  396. {
  397. char *malloc();
  398. struct line *line;
  399. line=(struct line *)charalloc(NULL,sizeof(struct line));
  400. line->up=line->down=(struct line *)NULL;
  401. line->text=(char *)NULL;
  402. line->updist=line->downdist=0;
  403. return(line);
  404. }
  405. char *
  406. charalloc(ptr,nbytes) /* if ptr==NULL, allocate new memory, if !=NULL
  407. * realloc. ; then check for errors */
  408. char *ptr;
  409. int nbytes;
  410. {
  411. char *malloc(),*realloc();
  412. if(ptr==NULL)
  413. ptr=malloc(nbytes);
  414. else
  415. ptr=realloc(ptr,nbytes);
  416. if(ptr==NULL)
  417. error("Can't allocate any more memory");
  418. return(ptr);
  419. }
  420. printup(line) /* recursively print lines above & including 'line' */
  421. struct line *line;
  422. {
  423. if(line) /* exit clause */
  424. {
  425. printup(line->up);
  426. printline(line);
  427. }
  428. }
  429. printdown(line) /* recursively print lines below & including 'line'*/
  430. struct line *line;
  431. {
  432. if(line)
  433. {
  434. printline(line);
  435. printdown(line->down);
  436. }
  437. }
  438. printline(line) /* print line->text and appropriate lf */
  439. register struct line *line;
  440. {
  441. int anytabs,t;
  442. int pos; /* used for space squeezing */
  443. register char c;
  444. register int i,spaces;
  445. static int inalt=0;
  446. /* i is used to point to element of line->text being printed out, col is the
  447. * cursor column
  448. */
  449. spaces=0;
  450. for(i=col=0; c=line->text[i]; i++,col++)
  451. {
  452. if(c&0200)
  453. {
  454. if(!inalt)
  455. {
  456. inalt=1;
  457. putchar(SO);
  458. }
  459. }
  460. else
  461. if(inalt)
  462. {
  463. inalt=0;
  464. putchar(SI);
  465. }
  466. c &= 0177; /* chop off high bit */
  467. if(squeeze)
  468. {
  469. switch(c)
  470. {
  471. case ' ':
  472. ++spaces;
  473. break;
  474. case '\b':
  475. col--;
  476. break;
  477. default:
  478. if(spaces)
  479. {
  480. pos=col-spaces;
  481. anytabs=t=((col-spaces)%8+spaces)/8;
  482. while(t--)
  483. {
  484. if(pos%8<7) /* make sure tab
  485. * isn't printed
  486. * for just one
  487. * space
  488. */
  489. putchar('\t');
  490. else
  491. putchar(' ');
  492. pos=0; /* flag so next time it
  493. * will always print tab
  494. */
  495. }
  496. if(anytabs)
  497. spaces=i%8;
  498. while(spaces--)
  499. putchar(' ');
  500. spaces=0;
  501. }
  502. break;
  503. }
  504. if(c!=' ')
  505. putchar(c);
  506. }
  507. else
  508. putchar(c);
  509. }
  510. if(half)
  511. switch(line->downdist)
  512. {
  513. case 0: /* last line of text */
  514. if(i>0)
  515. if(half && whichalf)
  516. printf("\033\071\r");
  517. else
  518. putchar('\n');
  519. break;
  520. case 1:
  521. printf("\033\071\r");
  522. break;
  523. case 2:
  524. putchar('\n');
  525. break;
  526. }
  527. else if(i>0 || line->down!=NULL) /* no \n if last line is blank */
  528. putchar('\n');
  529. }
  530. char *
  531. strinsert(str,in,c) /* insert c at str[in] */
  532. register char *str,c;
  533. int in;
  534. {
  535. int foo;
  536. register char tmp,*ptr;
  537. ptr=str+in;
  538. while(*ptr)
  539. {
  540. tmp = *ptr;
  541. *ptr++ = c;
  542. c=tmp;
  543. }
  544. if((ptr-str+2)>nextchunk)
  545. {
  546. str=charalloc(str,nextchunk+=CHUNKSIZE); /* alloc text space in
  547. * chunks of CHUNKSIZE, it's
  548. * faster than one by one
  549. */
  550. ptr=str+foo-2;
  551. }
  552. *ptr=c;
  553. *++ptr='\0';
  554. return(str);
  555. }
  556. char *
  557. forward(s,n) /* move forward n chars in s counting \b as moving back 1 */
  558. char *s;
  559. int n;
  560. {
  561. register int i;
  562. for(i=0; i<n || s[col]=='\b'; i++, col++)
  563. {
  564. if(!s[col])
  565. s=strinsert(s,col,' ');
  566. else if(s[col]=='\b' && i)
  567. i--;
  568. }
  569. return(s);
  570. }
  571. error(s,c) /* print error and exit */
  572. {
  573. fprintf(stderr,"col: %s %c\n",s,c);
  574. exit(1);
  575. }