home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume21 / strsed / part01 next >
Text File  |  1990-03-22  |  31KB  |  1,071 lines

  1. Subject:  v21i028:  String function to do sed(1)/tr(1) manipulations, Part01/03
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 2996bd1c 3a2bfb34 9ce711a4 292ee33f
  5.  
  6. Submitted-by: Terry Jones <terry@pcsbst.pcs.com>
  7. Posting-number: Volume 21, Issue 28
  8. Archive-name: strsed/part01
  9.  
  10. Strsed is another string function.  It does regular expression search and
  11. replace (including tr(1)-like transliteration) in the style of the
  12. ed/edit/ex text editors.
  13.  
  14. Here are a few examples:
  15.     strsed(s, "s/fred/joe/", 0)        Change "fred" to "joe"
  16.     strsed(s, "g/fred/joe/", 0)        Change ALL "fred" to "joe"
  17.     strsed(s, "g/a//", 0)        Delete all a's
  18.     strsed(s, "s/fred.*/{a-z}{A-Z}/", 0)
  19.         Change fred and what follows to upper case
  20.     int range[2];
  21.     strsed(s, "/[0-9]*/", range)
  22.         Search for a number in s; indices of the matched portion are
  23.         returned in range[0] & range[1].
  24.  
  25. It is general enough to be able to emulate most of the usual more
  26. complicated string functions (including strchr, strrchr, strpbrk, strspn,
  27. strcspn and strtok), and can perform complicated string manipulations that
  28. are otherwise a major headache.
  29.  
  30. The GNU regex package is needed for linking. The necessary source files
  31. are regex.c and regex.h which were included in the GNU 18.55 (and 18.54)
  32. emacs distribution.  Both are expected to be in the current directory when
  33. you type 'make'.
  34.  
  35. #!/bin/sh
  36. # shar:    Shell Archiver  (v1.22)
  37. #    Packed Thursday March,  8. 1990, 20:45:41  MEZ by terry
  38. #    from directory /user1/terry/s/regex
  39. #
  40. # This is part 1 of a multipart archive                                    
  41. # do not concatenate these parts, unpack them in order with /bin/sh        
  42. #
  43. #    Run the following text with /bin/sh to create:
  44. #      README
  45. #      strsed.3c
  46. #      strsed.c
  47. #      Makefile
  48. #      check1.c
  49. #      check2.c
  50. #      examples1
  51. #      examples2
  52. #      MANIFEST
  53. #
  54. if test -r s2_seq_.tmp
  55. then echo "Must unpack archives in sequence!"
  56.      next=`cat s2_seq_.tmp`; echo "Please unpack part $next next"
  57.      exit 1; fi
  58. echo "x - extracting README (Text)"
  59. sed 's/^X//' << 'SHAR_EOF' > README &&
  60. X
  61. XREADME for strsed
  62. X
  63. XStrsed is another string function. It does regular expression search
  64. Xand replace (including tr(1)-like transliteration) in the style of the
  65. Xed/edit/ex text editors.
  66. X
  67. XHere are a few examples:
  68. X
  69. X  (change "fred" to "joe")        strsed(s, "s/fred/joe/", 0);
  70. X  
  71. X  (change all "fred"s to "joe")   strsed(s, "g/fred/joe/", 0);
  72. X  
  73. X  (delete all a's)                strsed(s, "g/a//", 0);
  74. X  
  75. X  (change fred and what follows
  76. X   to upper case)                 strsed(s, "s/fred.*/{a-z}{A-Z}/", 0);
  77. X
  78. X  (strip repeated letters
  79. X   in the ranges a-z and 0-9)     strsed(s, "g/\([a-z0-9]\)\1+/\1/", 0);
  80. X
  81. X  (search for a number in s)      int range[2];
  82. X                                  strsed(s, "/[0-9]*/", range);
  83. X                                  /* indices of the matched portion are
  84. X                                     returned in range[0] & range[1] */
  85. X
  86. X
  87. Xand there's more...  See the man page in 'strsed.3c' for details.  
  88. X
  89. XIt is general enough to be able to emulate most of the usual more
  90. Xcomplicated string functions (including strchr, strrchr, strpbrk,
  91. Xstrspn, strcspn and strtok), and can perform complicated string
  92. Xmanipulations that are otherwise a major headache.
  93. X
  94. XThe GNU regex package is needed for linking. The necessary source
  95. Xfiles are regex.c and regex.h which were included in the GNU 18.55
  96. X(and 18.54) emacs distribution.  Both are expected to be in the
  97. Xcurrent directory when you type 'make'.
  98. X
  99. XStrsed is distributed with no conditions or guarantees attached. 
  100. XBear in mind however that the GNU license applies to the regex
  101. Xsources.
  102. X
  103. XThis was developed in a (basically) System V environment using gcc
  104. X(1.37), but there should be no problem at all in compiling it on BSD
  105. Xor elsewhere - everything is very standard.
  106. X
  107. XTo compile, copy or link the GNU regex.{c,h} files into the current
  108. Xdirectory and type 'make'. If you do not have gcc then change the
  109. XMakefile to use cc. Some compilers (NOT gcc) (notably MIPS' cc) have
  110. Xbeen known to have problems compiling regex.c into something that will
  111. Xrun without coredumping.
  112. X
  113. XThis will produce 'strsed.o'. As well as this, two checking programs
  114. Xwill be compiled and example files will be run. If all is well you
  115. Xwill get the two messages:
  116. X
  117. X  Substitution and transliteration tests passed successfully.
  118. X  Searching tests passed successfully.
  119. X
  120. XIndicating that things are OK.
  121. X
  122. X
  123. X
  124. XHave fun...
  125. X
  126. XTerry Jones 
  127. X(terry@pcsbst.pcs.com or ...!{pyramid,unido}!pcsbst!distel!terry)
  128. X
  129. XPCS Computer Systeme GmbH
  130. XPfalzer-Wald-Str 36
  131. X8000 Munchen 90
  132. XWest Germany       49-89-68004288
  133. SHAR_EOF
  134. chmod 0644 README || echo "restore of README fails"
  135. set `wc -c README`;Sum=$1
  136. if test "$Sum" != "2531"
  137. then echo original size 2531, current size $Sum;fi
  138. echo "x - extracting strsed.3c (Text)"
  139. sed 's/^X//' << 'SHAR_EOF' > strsed.3c &&
  140. X.\"
  141. X.\" $Header: /user1/terry/s/regex/RCS/strsed.3c,v 1.3 90/03/07 15:48:59 terry Exp Locker: terry $
  142. X.\"
  143. X.\"
  144. X.\" $Log:    strsed.3c,v $
  145. X.\\" Revision 1.3  90/03/07  15:48:59  terry
  146. X.\\" Lots of little things. 
  147. X.\\" 
  148. X.\\" Revision 1.2  90/03/06  22:48:00  terry
  149. X.\\" Changed the bit about the 3rd arg - which is now necessary.
  150. X.\\" 
  151. X.\\" Revision 1.1  90/01/18  20:03:54  terry
  152. X.\\" Initial revision
  153. X.\\" 
  154. X.\"
  155. X.TH STRSED 3C
  156. X.SH NAME
  157. Xstrsed \- ed(1)/tr(1)\-like substitute and replace function.
  158. X.SH SYNOPSIS
  159. X\fBchar *strsed(string, command, 0)
  160. X.br
  161. Xchar *string;
  162. X.br
  163. Xchar *command;\fR
  164. X.br
  165. X.sp
  166. Xor
  167. X.sp
  168. X\fBchar *strsed(string, command, range)
  169. X.br
  170. Xchar *string;
  171. X.br
  172. Xchar *command;
  173. X.br
  174. Xint range[2];\fR
  175. X.SH DESCRIPTION
  176. X.B Strsed 
  177. Xis a regular expression pattern match and replace
  178. Xfunction that also combines 
  179. X.B tr(1)
  180. Xlike transliteration. The GNU regex package is used 
  181. Xfor the regular expression matching.
  182. X.PP
  183. X.B Strsed
  184. Xcan be used to provide the
  185. Xfunctionality of most of the other more "complicated"
  186. Xstring functions (e.g. 
  187. X.BR strchr ,
  188. X.BR strrchr ,
  189. X.BR strpbrk ,
  190. X.BR strspn ,
  191. X.BR strcspn ,
  192. Xand
  193. X.BR strtok ),
  194. Xalthough less efficiently
  195. Xin each case, due to its generality.
  196. X.B Strsed
  197. Xis a very powerful and general function that can be used
  198. Xto carry out complicated string manipulations such as 
  199. Xthose that are possible in text editors.
  200. X.SH USAGE
  201. X.I String
  202. Xshould be a null\-terminated character string.
  203. XA copy is made and will be operated on according to the 
  204. Xsearch and replace instructions contained in 
  205. X.IR command .
  206. XUnless an error occurs (see ERRORS), the passed character strings
  207. X.I string 
  208. Xand
  209. X.I command
  210. Xare 
  211. X.I never
  212. Xcorrupted, and the string that is returned may always 
  213. Xbe passed to 
  214. X.B free()
  215. Xsince its space is obtained from 
  216. X.BR malloc() .
  217. X.PP
  218. XBoth 
  219. X.I string
  220. Xand 
  221. X.I command
  222. Xmay contain the following C\-like escape sequences:
  223. X.sp
  224. X    \eb      Backspace.
  225. X.br
  226. X    \ef      Formfeed.
  227. X.br
  228. X    \en      Newline.
  229. X.br
  230. X    \er      Carriage Return.
  231. X.br
  232. X    \es      Space.
  233. X.br
  234. X    \et      Horizontal Tab.
  235. X.br
  236. X    \ev      Vertical Tab.
  237. X.br
  238. X    \ez      Used to remove ambiguity if necessary.
  239. X.br
  240. X    \e0\-9    A reference to a register.
  241. X.br
  242. X             (except for \e0 in a regular expression.)
  243. X.br
  244. X    \e0x3d   The character whose value is 3d hexadecimal.
  245. X.br
  246. X    \e0X3d   The character whose value is 3d hexadecimal.
  247. X.br
  248. X    \e040    The character whose value is 40 octal.
  249. X.br
  250. X    \e32     The character whose value is 32 decimal.
  251. X.sp
  252. XThe NUL (0) character cannot be specified. 
  253. XA '\e' followed by one to three digits can be interpreted in
  254. Xseveral ways. If one or two hex digits are preceeded by 
  255. Xan 'x' or an 'X',
  256. Xthey will be taken as specifying a character in hexadecimal.
  257. XIf there are exactly three
  258. Xoctal digits and the first is in the range '0' to '3'
  259. Xthen they are taken as specifying a character in octal. 
  260. XOtherwise a single digit
  261. Xis taken to be a register reference and two or three digits
  262. Xare interpreted as specifying a character in decimal.
  263. X\ez can be used to 
  264. Xavoid problems with ambiguity. For instance,
  265. X.B \e007
  266. Xwill be interpreted by
  267. X.B strsed
  268. Xas octal 007. To specify the contents of register zero (\e0) 
  269. Xfollowed by the two characters "07", use 
  270. X.BR \e0\ez07 .
  271. XThe \ez makes it clear what is meant (acting like a punctuation mark)
  272. Xand is otherwise ignored.
  273. X.PP
  274. XStrsed allows 
  275. X.B ed(1) 
  276. Xlike regular expressions and substitutions
  277. Xon 
  278. X.IR string .
  279. XThe search and replace command is specified by
  280. X.IR command .
  281. XThe format of
  282. X.I command
  283. Xis either 
  284. X.sp
  285. X\fB/search_pattern/replacement/\fR
  286. X.br
  287. Xor
  288. X.br
  289. X\fBg/search_pattern/replacement/\fR
  290. X.PP
  291. XIn the first form, the search and replace is performed once on
  292. Xthe string, and in the second, the replacement is done globally
  293. X(i.e. for every occurrence of the search pattern in 
  294. X.IR string .).
  295. XA leading 
  296. X"s" in the above is silently ignored. This allows for a syntax
  297. Xmore like that of 
  298. X.BR ed(1) . 
  299. Xe.g. 
  300. X.I s/e/x/ 
  301. Xis the same as 
  302. X.IR /e/x/ .
  303. X.PP
  304. XIf 
  305. X.I replacement
  306. Xis empty, then the matched text will
  307. Xbe replaced by nothing \- i.e. deleted.
  308. X.PP
  309. X.I Search_pattern
  310. Xis a full regular expression
  311. X(see 
  312. X.BR ed(1) ), 
  313. Xincluding register
  314. Xspecifications (i.e. \fB\e( ... \e)\fR) and register references, 
  315. X(e.g. \fB\e2\fR)
  316. Xbut not the \fB{m,n}\fR 
  317. Xrepetition feature of 
  318. X.BR ed(1) .
  319. X.PP
  320. X.I Replacement
  321. Xconsists of ordinary characters and/or register references 
  322. X(e.g. \fB\e1\fR or \fB\e2\fR.) \fB\e0\fR means the entire matched 
  323. Xtext. In addition, a
  324. Xregister reference may be immediately followed by a 
  325. Xtransliteration request, of the form 
  326. X.br
  327. X\fB{char\-list\-1}{char\-list\-2}\fR.
  328. X.br
  329. XThe characters from 
  330. X.I char\-list\-1
  331. Xwill be transliterated into the corresponding ones from 
  332. X.I char\-list\-2 
  333. Xin the same manner as
  334. X.BR tr(1) . 
  335. XIf the register reference before a transliteration 
  336. Xrequest is omitted, it defaults to \fB\e0\fR.
  337. XWithin a transliteration request, the characters
  338. X"}" and "\-" are metacharacters and must be escaped with
  339. Xa leading \e if you want them to be interpreted literally.
  340. XCharacter ranges such as a\-z are expanded in the same
  341. Xfashion as 
  342. X.BR tr(1) .
  343. XIf 
  344. X.I char\-list\-2
  345. Xis shorter than 
  346. X.I char\-list\-1
  347. Xthen 
  348. X.I char\-list\-2
  349. Xis padded to be the same length as
  350. X.I char\-list\-1
  351. Xby repeating its last character as many times as are needed.
  352. XFor example, the transliteration request
  353. X.br
  354. X.B {a\-z}{X}
  355. X.br
  356. Xwill transliterate all lower case letters into an 'X'.
  357. XCharacter ranges may be increasing or decreasing.
  358. X.PP
  359. XUnusual character ranges (such as 
  360. X.BR a\-f\-0\-\e0x2d\-c )
  361. Xare interpreted as running from their first character
  362. Xto their last (so the above would be treated as 
  363. X.BR a\-c ).
  364. XNote that it is
  365. X.B not
  366. Xpossible (in this release) to specify the complement of a 
  367. Xcharacter range in a transliteration request. However, this 
  368. Xcan be done in the
  369. X.I search_pattern
  370. Xby commencing a character class with a "^" in the normal
  371. Xregular expression fashion.
  372. X.PP
  373. XThe highest register that can be referenced is \fB\e9\fR.
  374. X.SH EXAMPLES
  375. XHere are some example
  376. X.I command
  377. Xstrings that might be given to 
  378. X.BR strsed .
  379. X.sp
  380. X\fB/a/A/\fR                    # Change the first 'a' into an 'A'
  381. X.br
  382. X\fBg/a/A/\fR                   # Change every 'a' into an 'A'
  383. X.br
  384. X\fBg/://\fR                     # Delete every ':'
  385. X.br
  386. X\fBg/jack/jill/\fR             # Change every 'jack' to a 'jill'
  387. X.br
  388. X\fB/[^\es\et]/X/\fR              # Change the first non\-whitespace character into an 'X'.
  389. X.sp
  390. XSome more advanced examples...
  391. X.sp
  392. X\fB/\e([\es\et]*\e)\e([^\es\et]*\e)/\e1\e2{a\-z}{A\-Z}/\fR
  393. X.sp
  394. XThis converts the first non\-whitespace word to upper case,
  395. Xpreserving any initial whitespace.
  396. XIt catches the first run of spaces and TABs into register
  397. Xone \e([\es\et]*\e), and then the following run of non\-white
  398. Xcharacters into register two \e([^\es\et]*\e). The replacement,
  399. X\e1\e2{a\-z}{A\-Z} specifies register 1 (the whitespace) followed
  400. Xby the contents of register 2 transliterated into uppercase.
  401. XThis would produce 
  402. X.br
  403. X"   SPOTTED pinto bean" 
  404. X.br
  405. Xif called on the string 
  406. X.br
  407. X"   spotted pinto bean".
  408. X.sp
  409. X\fBg/\e([a\-z]\e)\e1+/\e1/\fR
  410. X.sp
  411. XThis is a very useful example and performs the same function
  412. Xas tr \-s. That is, it squeezes runs of identical characters
  413. X(in the range a to z) down to a single instance of that 
  414. Xcharacter. So "beeee good" becomes "be god". The "+" is the
  415. Xregular expression notation meaning "one or more".
  416. X.sp
  417. X\fBg/\e([\et\es]*\e)\e(.\e)\e([^\et\es]*\e)/\e1\e2{a\-z}{A\-Z}\e3/\fR
  418. X.sp
  419. XThis example capitalises the first letter of each word in the string,
  420. Xand preserves all whitespace. It catches three things,
  421. X.br
  422. X1) the initial whitespace         \e([\et\es]*\e)  in register 1
  423. X.br
  424. X2) the next letter                \e(.\e)        in register 2
  425. X.br
  426. X3) the following nonwhite letters \e([^\et\es]*\e) in register 3
  427. X.br
  428. Xand then prints them out as they were found, with the only
  429. Xdifference being the uppercase conversion of the contents of
  430. Xregister 2. Given the string
  431. X.br
  432. X"  this is a line  " 
  433. X.br
  434. Xthis command would
  435. Xreturn 
  436. X.br
  437. X"  This Is A Line  ".
  438. X.br
  439. XIf the initial 'g' was not present in the command, then the capitalisation
  440. Xwould only be done to the first word in the string. 
  441. XIt is important to understand this difference well.
  442. X.SH SEARCHING ONLY
  443. X.B Strsed 
  444. Xmay be used to search for a regular expression in a
  445. Xstring, but perform no action. The portion of the string
  446. Xthat matched will be returned in the third argument
  447. X.IR range .
  448. XIn this case 
  449. X.I command
  450. Xshould be of the form 
  451. X.IR /pattern/ .
  452. XOn return, 
  453. X.I range[0] 
  454. Xwill contain an index into the original
  455. Xstring to indicate where the match began, and 
  456. X.I range[1] 
  457. Xwill index the first character after the end of the match. For
  458. Xexample, after the call 
  459. X.sp
  460. Xstrsed("two big macs please", "/b.*c/", range);
  461. X.sp
  462. X.I range[0] 
  463. Xwill contain 4 and 
  464. X.I range[1]
  465. Xwill contain 11.
  466. XIf not match is found, both elements of
  467. X.I range
  468. Xwill contain \-1.
  469. X.SH ERRORS
  470. XIf 
  471. X.B strsed 
  472. Xdetects any error it returns NULL. This can happen if the
  473. Xsyntax of 
  474. X.I command
  475. Xis incorrect, if the regular expression in 
  476. X.I command
  477. Xis incorrect, if space cannot be obtained from
  478. X.BR malloc ,
  479. Xor for other similar reasons. Note that it is 
  480. X.B not
  481. Xan error if the empty string is returned.
  482. X.SH "COMPILING AND LINKING STRSED"
  483. X.B Strsed
  484. Xshould be compiled with the \-O and \-c options of your C compiler.
  485. XIt has no main() function. When you come to link, you use strsed.o 
  486. Xand regex.o from the GNU 18.55 (or 18.54) emacs distribution.
  487. X.SH "OBSCURE NOTE ON REGULAR EXPRESSIONS"
  488. XIt is possible (but not too likely) that the regular expression 
  489. Xlanguage that is recognised may differ
  490. Xslightly from installation to installation. This is because the 
  491. XGNU regular expression package may compiled with different settings
  492. Xfor recognition of meta-characters. So on one machine, the character
  493. X"|" might be taken as being the OR operator, whilst somewhere else
  494. Xyou need to give "\e|" \- or vice-versa. This could be a pain in the
  495. Xneck, but there's not alot that can be done about it. If you 
  496. X.I really 
  497. Xneed
  498. Xto know the difference in a portable way, look in regex.h to see
  499. Xwhat things are defined and then act accordingly when constructing 
  500. Xcommands for 
  501. X.BR strsed . 
  502. X.SH AUTHOR
  503. XTerry Jones
  504. X.br
  505. XPCS Computer Systeme GmbH
  506. X.br
  507. XPfaelzer\-Wald\-Str 36
  508. X.br
  509. X8000 Muenchen 90
  510. X.br
  511. XWest Germany       49\-89\-68004288
  512. X.sp
  513. Xterry@distel.pcs.com  or ...!{pyramid,unido}!pcsbst!distel!terry
  514. X.sp
  515. XJanuary 8th, 1990.
  516. X.SH ACKNOWLEDGEMENTS
  517. XMany thanks to Jordan K. (mother) Hubbard for discussions, bugfinding,
  518. Xhandholding, forcing me to use emacs and torrents of (usually) uncalled\-for abuse.
  519. X.SH SEE ALSO
  520. X.I ed(1), tr(1)
  521. SHAR_EOF
  522. chmod 0644 strsed.3c || echo "restore of strsed.3c fails"
  523. set `wc -c strsed.3c`;Sum=$1
  524. if test "$Sum" != "10496"
  525. then echo original size 10496, current size $Sum;fi
  526. echo "x - extracting strsed.c (Text)"
  527. sed 's/^X//' << 'SHAR_EOF' > strsed.c &&
  528. X#ifndef lint
  529. Xstatic char *rcsid = "$Header: /user1/terry/s/regex/RCS/strsed.c,v 1.17 90/03/08 20:44:32 terry Exp Locker: terry $";
  530. X#endif lint
  531. X
  532. X/*
  533. X * Strsed.c
  534. X *
  535. X *     ed(1)/tr(1)-like search, replace, transliterate. See the
  536. X *     manpage for details.
  537. X *
  538. X * Usage:
  539. X *
  540. X *        strsed(string, pattern, 0);
  541. X *        char *string;
  542. X *        char *pattern;
  543. X * or
  544. X *        strsed(string, pattern, range);
  545. X *        char *string;
  546. X *        char *pattern;
  547. X *        int  range[2];
  548. X *
  549. X *
  550. X * Terry Jones    
  551. X * terry@distel.pcs.com
  552. X * ...!{pyramid,unido}!pcsbst!distel!terry
  553. X *
  554. X * PCS Computer Systeme GmbH
  555. X * Pfaelzer-Wald-Str 36
  556. X * 8000 Muenchen 90
  557. X * West Germany       49-89-68004288
  558. X *
  559. X * January 8th, 1990.
  560. X *
  561. X */
  562. X
  563. X/*
  564. X * $Log:    strsed.c,v $
  565. X * Revision 1.17  90/03/08  20:44:32  terry
  566. X * Final cleanup.
  567. X * 
  568. X * Revision 1.16  90/03/07  15:46:35  terry
  569. X * Changed backslash_eliminate to only malloc on 
  570. X * REPLACEMENT type. Added ".*" optimisation so that
  571. X * the regex functions are never called.
  572. X * 
  573. X * Revision 1.15  90/03/06  22:27:49  terry
  574. X * Removed varargs stuff since the 3rd argument is now 
  575. X * compulsory. Cleaned up. A few comments even.
  576. X * 
  577. X * Revision 1.14  90/03/06  21:50:28  terry
  578. X * Touched up memory stuff. Added mem_find(). Changed
  579. X * buf_sz and buf_inc to be a reasonable refelection
  580. X * of the length of the input.
  581. X * 
  582. X * Revision 1.13  90/03/06  20:22:48  terry
  583. X * Major rearrangements. Added mem(), mem_init(), mem_save(),
  584. X * mem_free() to handle memory in a vastly improved fashion.
  585. X * Calls to malloc are minimised as far as possible.
  586. X * 
  587. X * Revision 1.12  90/03/06  13:23:33  terry
  588. X * Made map static.
  589. X * 
  590. X * Revision 1.11  90/01/10  15:51:12  terry
  591. X * checked in with -k by terry at 90.01.18.20.03.08.
  592. X * 
  593. X * Revision 1.11  90/01/10  15:51:12  terry
  594. X * *** empty log message ***
  595. X * 
  596. X * Revision 1.10  90/01/10  12:48:40  terry
  597. X * Fixed handling of perverted character ranges in nextch().
  598. X * a-f-c now means a-c.
  599. X * 
  600. X * Revision 1.9  90/01/10  12:03:48  terry
  601. X * Pounded on space allocation, added more_space,
  602. X * remove free() in build_map, tested tiny buffer sizes etc.
  603. X * 
  604. X * Revision 1.8  90/01/09  18:15:12  terry
  605. X * added backslash elimination to str.
  606. X * altered backslash_elimantion to take one of three types
  607. X * REGEX, NORMAL or REPLACEMENT depending on the
  608. X * elimination desired. Changed interpretation of \ 
  609. X * followed by a single digit to be that character if the
  610. X * type of elimination is NORMAL. i.e. \4 = ^D.
  611. X * 
  612. X * Revision 1.7  90/01/09  17:05:05  terry
  613. X * Frozen version for release to comp.sources.unix
  614. X * 
  615. X * Revision 1.6  90/01/09  16:47:54  terry
  616. X * Altered pure searching return values to be -1
  617. X * 
  618. X * Revision 1.5  90/01/09  14:54:34  terry
  619. X * *** empty log message ***
  620. X * 
  621. X * Revision 1.4  90/01/09  14:51:04  terry
  622. X * removed #include <stdio> silliness.
  623. X * 
  624. X * Revision 1.2  90/01/09  10:48:22  terry
  625. X * Fixed handling of } and - metacharacters inside
  626. X * transliteration request strings in backslash_eliminate().
  627. X * 
  628. X * Revision 1.1  90/01/08  17:41:35  terry
  629. X * Initial revision
  630. X * 
  631. X *
  632. X */
  633. X
  634. X#include <ctype.h>
  635. X#include <string.h>
  636. X#include <malloc.h>
  637. X#include "regex.h"
  638. X
  639. X#define BYTEWIDTH     8
  640. X#define REGEX         0
  641. X#define REPLACEMENT   1
  642. X#define NORMAL        2
  643. X
  644. X/*
  645. X * And this is supposed to make freeing easier. It's a little hard to
  646. X * keep track of what can and cannot be freed in what follows, so I
  647. X * ignore it and every time a malloc is done for one of the things
  648. X * below (and these are the only ones possible) we free if need be and
  649. X * then alloc some more if it can't be avoided. No-one (who is going 
  650. X * to free) needs to call malloc then. And no-one need call free. 
  651. X * Wonderful in theory...
  652. X */
  653. X
  654. X#define MEM_STR       0
  655. X#define MEM_PAT       1
  656. X#define MEM_FROM      2
  657. X#define MEM_TO        3
  658. X#define MEM_NEWSTR    4
  659. X#define MEM_MAP       5
  660. X#define MEM_MAP_SAVE  6
  661. X
  662. X#define MEM_SLOTS     7
  663. X
  664. X/*
  665. X * This calls mem_free(), which free()s all the allocated storage EXCEPT
  666. X * for the piece whose address is 'n'. If something goes wrong below
  667. X * we call RETURN(0) and if we want to return some address we call RETURN
  668. X * with the address to be returned.
  669. X */
  670. X
  671. X#define RETURN(n)     \
  672. X    mem_free(n);      \
  673. X    return (char *)n;
  674. X
  675. Xstatic struct {
  676. X    char *s;
  677. X    int size;
  678. X    int used;
  679. X} mem_slots[MEM_SLOTS];
  680. X
  681. X
  682. X#define more_space(need)                                                   \
  683. X    if (need && space != -1){                                              \
  684. X        if (space - (need) < 0){                                           \
  685. X            buf_sz += buf_inc + (need) - space;                            \
  686. X            if (!(new_str = (char *)realloc(new_str, (unsigned)buf_sz))){  \
  687. X                RETURN(0);                                                 \
  688. X            }                                                              \
  689. X        mem_slots[MEM_NEWSTR].s = new_str;                             \
  690. X        mem_slots[MEM_NEWSTR].size = buf_sz;                           \
  691. X            space = buf_inc;                                               \
  692. X        }                                                                  \
  693. X        else{                                                              \
  694. X            space -= need;                                                 \
  695. X        }                                                                  \
  696. X    }
  697. X
  698. X
  699. Xchar *
  700. Xstrsed(string, pattern, range)
  701. Xregister char *string;
  702. Xregister char *pattern;
  703. Xint *range;
  704. X{
  705. X    extern char *re_compile_pattern();
  706. X    extern int re_search();
  707. X
  708. X    static char *backslash_eliminate();
  709. X    static char *mem();
  710. X    static void mem_init();
  711. X    static void mem_free();
  712. X    
  713. X    char *from;
  714. X    char *new_str;
  715. X    char *pat;
  716. X    char *str;
  717. X    char *tmp;
  718. X    char *to;
  719. X    static char map[1 << BYTEWIDTH];
  720. X    int buf_sz;
  721. X    int buf_inc;
  722. X    int global = 0;
  723. X    int match;
  724. X    int new_pos = 0;
  725. X    int search_only = 0;
  726. X    int seenbs = 0;
  727. X    int space;
  728. X    int match_all = 0;
  729. X    register int str_len;
  730. X    static int first_time = 1;
  731. X    static struct re_pattern_buffer re_comp_buf;
  732. X    struct re_registers regs;
  733. X
  734. X    if (!string || !pattern){
  735. X        RETURN(0);
  736. X    }
  737. X    
  738. X    /*
  739. X     * If this is the first time we've been called, clear the memory slots.
  740. X     */
  741. X    if (first_time){
  742. X    mem_init();
  743. X    }
  744. X
  745. X    /*
  746. X     * Take our own copies of the string and pattern since we promised
  747. X     * in the man page not to hurt the originals.
  748. X     */
  749. X    str = mem(MEM_STR, strlen(string) + 1);
  750. X    str[0] = '\0';
  751. X    strcat(str, string);
  752. X    pat = mem(MEM_PAT, strlen(pattern) + 1);
  753. X    pat[0] = '\0';
  754. X    strcat(pat, pattern);
  755. X
  756. X    /*
  757. X     * If escape sequences are not already removed elsewhere, remove
  758. X     * them from the string. If you don't know what you're doing here
  759. X     * or are in any doubt, don't define ESCAPED_STRING.
  760. X     */
  761. X#ifndef ESCAPED_STRING
  762. X    if (!(str = backslash_eliminate(str, NORMAL, MEM_STR))){
  763. X        RETURN(0);
  764. X    }
  765. X#endif
  766. X
  767. X    str_len = strlen(str);
  768. X    
  769. X    /*
  770. X     * Set up the size of our buffer (in which we build the
  771. X     * newstring, and the size by which we increment it when
  772. X     * (and if) the need arises. There shouldn't be too much
  773. X     * growth in the average case. Of course some people will
  774. X     * go and do things like 
  775. X     *
  776. X     * strsed(string, "s/.*$/\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")
  777. X     *
  778. X     * and they will be somewhat penalised. Oh well.
  779. X     *
  780. X     */
  781. X
  782. X    buf_sz = str_len < 8 ? 16 : str_len << 1;
  783. X    buf_inc = buf_sz;
  784. X
  785. X    /*
  786. X     * Get the action. 
  787. X     * s = substitue and g = global.
  788. X     * anything else is invalid.
  789. X     *
  790. X     */
  791. X    while (*pat && *pat != '/'){
  792. X        switch (*pat){
  793. X            case 'g':{
  794. X                global = 1;
  795. X                break;
  796. X            }
  797. X            case 's':{
  798. X                break;
  799. X            }
  800. X            default:{
  801. X                RETURN(0);
  802. X            }
  803. X        }
  804. X        pat++;
  805. X    }
  806. X
  807. X    if (!*pat){
  808. X        RETURN(0);
  809. X    }
  810. X
  811. X    pat++;
  812. X
  813. X    /*
  814. X     * Now split 'pat' into its two components. These are delimited (or
  815. X     * should be) by (unquoted) '/'. The first we point to with 'from' 
  816. X     * and the second with 'to'. 
  817. X     *
  818. X     * Someone should write a function to make this sort of thing trivial...
  819. X     *
  820. X     */
  821. X    
  822. X    from = to = pat;
  823. X
  824. X    while (*to){
  825. X        if (seenbs){
  826. X            seenbs = 0;
  827. X        }
  828. X        else{
  829. X            if (*to == '\\'){
  830. X                seenbs = 1;
  831. X            }
  832. X            else if (*to == '/'){
  833. X                break;
  834. X            }
  835. X        }
  836. X        to++;
  837. X    }
  838. X
  839. X    if (!*to){
  840. X        RETURN(0);
  841. X    }
  842. X
  843. X    *to++ = '\0';
  844. X
  845. X    if (*to){
  846. X        tmp = to + strlen(to) - 1;
  847. X
  848. X        /*
  849. X         * Back up to the last non-whitespace char in 'to'
  850. X         *
  851. X         */
  852. X
  853. X        while (*tmp == ' ' || *tmp == '\t'){
  854. X            tmp--;
  855. X        }
  856. X
  857. X        /*
  858. X         * Make sure that, if there was a character,
  859. X         * that it was a / and wasn't preceded by \.
  860. X         *
  861. X        */
  862. X
  863. X        if (*tmp && !(*tmp = '/' && *(tmp - 1) != '\\')){
  864. X            RETURN(0);
  865. X        }
  866. X
  867. X        *tmp = '\0';
  868. X    }
  869. X    else{
  870. X        /*
  871. X     * Search only.
  872. X         * It doesn't make sense to say
  873. X         *
  874. X         * strsed(string, "g/abc/", range)
  875. X         *
  876. X         * because we are only searching and returning the 
  877. X         * matched indexes. So turn off global (in case it's on)
  878. X     * so that we will return just the first instance.
  879. X     *
  880. X     * If no range has been given either, then there's no
  881. X     * point in going on.
  882. X         *
  883. X         */
  884. X    
  885. X    if (!range){
  886. X        RETURN(0);
  887. X    }
  888. X    
  889. X        global = 0;
  890. X        search_only = 1;
  891. X    }
  892. X
  893. X    /*
  894. X     * Eliminate backslashes and character ranges etc.
  895. X     *
  896. X     */
  897. X
  898. X    if (!(from = backslash_eliminate(from, REGEX, MEM_FROM)) || 
  899. X        !(to = backslash_eliminate(to, REPLACEMENT, MEM_TO))){
  900. X        RETURN(0);
  901. X    }
  902. X    
  903. X    /*
  904. X     * If the first char of 'to' is '\0' then we are deleting or 
  905. X     * searching only. We don't have to worry about space since 
  906. X     * the transformed string will be less than or equal in length
  907. X     * to the original. We just overwrite.
  908. X     * We set space = -1 so that later on we can avoid worrying
  909. X     * about overflow etc.
  910. X     *
  911. X     * Otherwise, we are doing a substitution. Here we have to
  912. X     * worry about space because the replacement may be larger
  913. X     * than the original. malloc some room and if we overflow it 
  914. X     * later we will realloc. slows things down if the new string
  915. X     * turns out to be too much bigger. oh well.
  916. X     *
  917. X     */
  918. X    
  919. X    if (*to){
  920. X        if (!(new_str = mem(MEM_NEWSTR, buf_sz + 1))){
  921. X            RETURN(0);
  922. X        }
  923. X        space = buf_sz;
  924. X    }
  925. X    else{
  926. X        new_str = str;
  927. X        space = -1;
  928. X    }
  929. X
  930. X    /*
  931. X     * Do things to get ready for the regex functions.
  932. X     * Don't do anything though if the regex in 'from' is ".*"
  933. X     * We handle that below. (Just a special case optimisation).
  934. X     *
  935. X     */
  936. X
  937. X    if (from[0] == '.' && from[1] == '*' && from[2] == '\0'){
  938. X    register int i; 
  939. X    match_all = 1;
  940. X    /*
  941. X     * For safety's sake, clear out the register values.
  942. X     * There might be a register reference in the replacement. 
  943. X     * There will be nothing in the register (since the search
  944. X     * pattern was ".*"). Since we aren't calling the regex 
  945. X     * stuff we can't rely on it to set these to -1.
  946. X     */
  947. X    for (i = 0; i < RE_NREGS; i++){
  948. X        regs.start[i] = -1;
  949. X    }
  950. X    }
  951. X    else{
  952. X    if (first_time){
  953. X        if (!(re_comp_buf.buffer = (char *)malloc((unsigned)200))){
  954. X        RETURN(0);
  955. X        }
  956. X        
  957. X        re_comp_buf.allocated = 200;
  958. X        
  959. X        if (!(re_comp_buf.fastmap = (char *)malloc((unsigned)1 << BYTEWIDTH))){
  960. X        RETURN(0);
  961. X        }
  962. X        
  963. X        first_time = 0;
  964. X    }
  965. X    
  966. X    re_comp_buf.translate = 0;
  967. X    re_comp_buf.used = 0;
  968. X
  969. X    /*
  970. X     * Compile the r.e. 
  971. X     *
  972. X     */
  973. X    if (re_compile_pattern(from, strlen(from), &re_comp_buf)){
  974. X        RETURN(0);
  975. X    }
  976. X    }
  977. X    
  978. X
  979. X    /*
  980. X     * Now get on with the matching/replacing etc.
  981. X     *
  982. X     */
  983. X
  984. X    do {
  985. X    if (match_all){
  986. X        /* Fake a match instead of calling re_search(). */
  987. X        match = 1;
  988. X        regs.start[0] = 0;
  989. X        regs.end[0] = str_len;
  990. X    }
  991. X    else{
  992. X        match = re_search(&re_comp_buf, str, str_len, 0, str_len, ®s);
  993. X    }
  994. X
  995. X        if (search_only){
  996. X            /*
  997. X             * Show what happened and return.
  998. X             *
  999. X             */
  1000. X        
  1001. X        range[0] = match == -1 ? -1 : regs.start[0];
  1002. X        range[1] = match == -1 ? -1 : regs.end[0];
  1003. X            RETURN(str);
  1004. X        }
  1005. X
  1006. X        if (match != -1){
  1007. X
  1008. X            /*
  1009. X             * Copy that portion that was not matched. It will
  1010. X             * be unchanged in the output string.
  1011. X             *
  1012. X             */
  1013. X            more_space(regs.start[0]);
  1014. X            strncpy(new_str + new_pos, str, regs.start[0]);
  1015. X            new_pos += regs.start[0];
  1016. X
  1017. X            /*
  1018. X             * Put in the replacement text (if any).
  1019. X             * We substitute the contents of 'to', watching for register
  1020. X             * references.
  1021. X             */
  1022. X
  1023. X            tmp = to;
  1024. X            while (*tmp){
  1025. X                if (*tmp == '\\' && isdigit(*(tmp + 1))){
  1026. X
  1027. X                    /* A register reference. */
  1028. X
  1029. X                    register int reg = *(tmp + 1) - '0';
  1030. X                    int translit = 0;
  1031. X                    int need = regs.end[reg] - regs.start[reg];
  1032. X
  1033. X                    /*
  1034. X                     * Check for a transliteration request.
  1035. X                     *
  1036. X                     */
  1037. X            if (*(tmp + 2) == '{'){
  1038. X            /* A transliteration table. Build the map. */
  1039. X            static char *build_map();
  1040. X            if (!(tmp = build_map(tmp + 2, map))){
  1041. X                RETURN(0);
  1042. X            }
  1043. X            translit = 1;
  1044. X            }
  1045. X            else{
  1046. X            tmp += 2;
  1047. X            translit = 0;
  1048. X            }
  1049. X
  1050. X            more_space(need);
  1051. X            
  1052. X            /*
  1053. X             * Copy in the register contents (if it matched), transliterating if need be.
  1054. X             *
  1055. X             */
  1056. X                    if (regs.start[reg] != -1){
  1057. X            register int i;
  1058. X                        for (i = regs.start[reg]; i < regs.end[reg]; i++){
  1059. X                            new_str[new_pos++] = translit ? map[str[i]] : str[i];
  1060. X                        }
  1061. X                    }
  1062. X                }
  1063. X                else{
  1064. X                    /* A plain character, put it in. */
  1065. SHAR_EOF
  1066. echo "End of part 1"
  1067. echo "File strsed.c is continued in part 2"
  1068. echo "2" > s2_seq_.tmp
  1069. exit 0
  1070.  
  1071.