1 //******************************************************************************
2 // Copyright 1996-2005,2021 by Thomas E. Dickey *
3 // All Rights Reserved. *
4 // *
5 // Permission to use, copy, modify, and distribute this software and its *
6 // documentation for any purpose and without fee is hereby granted, provided *
7 // that the above copyright notice appear in all copies and that both that *
8 // copyright notice and this permission notice appear in supporting *
9 // documentation, and that the name of the above listed copyright holder(s) *
10 // not be used in advertising or publicity pertaining to distribution of the *
11 // software without specific, written prior permission. THE ABOVE LISTED *
12 // COPYRIGHT HOLDER(S) DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, *
13 // INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO *
14 // EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY SPECIAL, *
15 // INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM *
16 // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE *
17 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR *
18 // PERFORMANCE OF THIS SOFTWARE. *
19 //******************************************************************************
20 // $Id: tabs.cpp,v 1.26 2021/01/08 23:32:37 tom Exp $
21 // Tab conversion & first-pass scanning
22
23 #include <ctype.h>
24 #include <string.h> // strlen(), strstr(), strchr(), strcpy(), strcmp()
25
26 #include "bcpp.h"
27
28 // ----------------------------------------------------------------------------
29 // Function takes a unsigned char and converts it to a C type string that
30 // contains the char's value, but in octal (i.e "\000" = null char).
31 //
32 // Parameters:
33 // value : The value that wishes to be converted
34 //
35 // Return Values:
36 // char* : Returns a pointer to the string that was converted.
37 // Memory is allocated via the new command, and once string has been used,
38 // memory should be returned to the system.
39 //
ConvertCharToOctal(unsigned char value)40 static char* ConvertCharToOctal (unsigned char value)
41 {
42 const char octalVals[] = "01234567";
43
44 char* pOctalValue = new char[5]; // \000 digits plus null terminator
45
46 if (pOctalValue != NULL)
47 {
48 int last = 1;
49 switch (value)
50 {
51 case '\a': pOctalValue[1] = 'a'; break;
52 case '\b': pOctalValue[1] = 'b'; break;
53 case '\f': pOctalValue[1] = 'f'; break;
54 case '\n': pOctalValue[1] = 'n'; break;
55 case '\r': pOctalValue[1] = 'r'; break;
56 case '\t': pOctalValue[1] = 't'; break;
57 default:
58 last = 3;
59 for (int pos = last; pos >= 1; pos--)
60 {
61 pOctalValue[pos] = octalVals[(value & 7)];
62 value >>= 3; // left shift to next three bits
63 }
64 }
65 pOctalValue[0] = ESCAPE;
66 pOctalValue[last+1] = NULLC;
67 }
68
69 return pOctalValue;
70 }
71
72 // ----------------------------------------------------------------------------
73 // Compute the number of characters in an escape
skipEscape(char * String)74 static int skipEscape(char *String)
75 {
76 int it = 1;
77 int n = 1;
78
79 if (isdigit(String[n]))
80 {
81 while (n <= 3)
82 {
83 if (isdigit(String[n++]))
84 it = n-1;
85 else
86 break;
87 }
88 }
89 else if (String[n] == 'x')
90 {
91 while (n <= 3)
92 {
93 if (isxdigit(String[++n]))
94 it = n-1;
95 else
96 break;
97 }
98 }
99 return it;
100 }
101
102 // ----------------------------------------------------------------------------
103 #ifdef DEBUG
showCharState(CharState theState)104 static const char *showCharState(CharState theState)
105 {
106 const char *it;
107 switch (theState)
108 {
109 default:
110 case Blank: it = "Blank"; break;
111 case PreProc: it = "PreProc"; break;
112 case Normal: it = "Normal"; break;
113 case Comment: it = "Comment"; break;
114 case Ignore: it = "Ignore"; break;
115 case DQuoted: it = "DQuoted"; break;
116 case SQuoted: it = "SQuoted"; break;
117 }
118 return it;
119 }
120 #endif
121
122 // ----------------------------------------------------------------------------
123 // Compute the state after this character is processed
nextCharState(char * & String,CharState & theState,int & skip)124 static void nextCharState(char * &String, CharState &theState, int &skip)
125 {
126 if (skip-- <= 0)
127 {
128 skip = 0;
129 if (theState == Blank)
130 {
131 if (!isspace(String[0]))
132 theState = Normal;
133 }
134 if (theState == Normal)
135 {
136 switch (String[0])
137 {
138 case ESCAPE:
139 skip = skipEscape(String);
140 break;
141 case DQUOTE:
142 theState = DQuoted;
143 break;
144 case SQUOTE:
145 theState = SQuoted;
146 break;
147 case '/':
148 switch (String[1])
149 {
150 case '*':
151 theState = Comment;
152 skip = 1;
153 break;
154 case '/':
155 theState = Ignore;
156 break;
157 default:
158 break;
159 }
160 break;
161 case SPACE:
162 case TAB:
163 theState = Blank;
164 break;
165 default:
166 break;
167 }
168 }
169 else if (theState == Comment)
170 {
171 if (String[0] == '*'
172 && String[1] == '/')
173 {
174 theState = Normal;
175 skip = 1;
176 }
177 }
178 else if (theState == SQuoted)
179 {
180 switch (String[0])
181 {
182 case SQUOTE:
183 theState = Normal;
184 break;
185 case ESCAPE:
186 skip = skipEscape(String);
187 break;
188 default:
189 break;
190 }
191 }
192 else if (theState == DQuoted)
193 {
194 switch (String[0])
195 {
196 case DQUOTE:
197 theState = Normal;
198 break;
199 case ESCAPE:
200 skip = skipEscape(String);
201 break;
202 default:
203 break;
204 }
205 }
206 }
207 }
208
209 // ----------------------------------------------------------------------------
210 // Check for non-printable characters, which we'll either quote or remove,
211 // depending on whether they're in strings or not.
NonPrintable(char c,int mode)212 static int NonPrintable(char c, int mode)
213 {
214 int it = false;
215 unsigned char check = static_cast<unsigned char>(c);
216
217 // remove chars below a space, but not if char is a TAB.
218 if (check < SPACE && check != TAB) {
219 it = true;
220 } else if (mode == 1) {
221 it = (check >= 127); /* non 7-bit ASCII? */
222 } else if (mode == 3) {
223 it = (check >= 127) && (check < 160); /* ISO C1 character? */
224 }
225 return it;
226 }
227
228 // ----------------------------------------------------------------------------
229 // Function expands tabs to spaces; the number of spaces to expand to is
230 // dependent upon the tabSpaceSize parameter within user settings, and
231 // tab column positions.
232 //
233 // Parameters:
234 // pString : Pointer to the string to process !
235 // tabLen : How much a tab is worth in spaces.
236 // deleteChars : mode to select non-printing characters for removal/quoting
237 // quoteChars : quote non-printing characters
238 // curState : character-state at beginning (end) of string
239 // lineState : character-states within string
240 //
241 // curState and lineState are set as side-effects
242 //
ExpandTabs(char * & pString,int tabLen,int deleteChars,bool quoteChars,CharState & curState,char * & lineState,bool & codeOnLine)243 void ExpandTabs (char* &pString,
244 int tabLen,
245 int deleteChars,
246 bool quoteChars,
247 CharState &curState, char * &lineState, bool &codeOnLine)
248 {
249 int col = 0;
250 int skip = 0;
251 size_t last = 0;
252 char* pSTab = pString;
253 bool expand = true;
254 bool my_pString = false;
255 bool had_print = false;
256 CharState oldState = curState;
257
258 lineState = new char[strlen (pString) + 1];
259 if (lineState == 0)
260 return;
261
262 lineState[0] = NullC;
263
264 //TRACE((" ExpandTabs(%s)%s\n", pString, codeOnLine ? " code" : ""))
265 while (*pSTab != NULLC)
266 {
267 col++;
268
269 if (isgraph(*pSTab))
270 had_print = true;
271
272 if (skip || !isspace(*pSTab))
273 last = col + skip;
274
275 if (*pSTab == TAB // calculate tab positions !
276 && expand
277 && skip == 0
278 && !(had_print && (curState == Ignore || curState == Comment))
279 && curState != SQuoted
280 && curState != DQuoted)
281 {
282 int tabAmount = 0;
283
284 // tab is first character !!!!
285 if (col == 1)
286 tabAmount = tabLen;
287 else
288 tabAmount = ((((col+tabLen-1) / tabLen)) * tabLen) - col + 1;
289
290 //TRACE(("amount:%d, col:%d, state:%s (%d)\n", tabAmount, col, showCharState(curState), had_print))
291 if (tabAmount > 0)
292 {
293 // create newString, remove tab !
294 char* pNewString = new char[strlen (pString) + tabAmount + 1];
295 char* pNewStates = new char[strlen (pString) + tabAmount + 1];
296
297 if (pNewString == NULL
298 || pNewStates == NULL)
299 {
300 if (my_pString)
301 {
302 delete[] pString;
303 pString = 0;
304 }
305 delete[] lineState;
306 return;
307 }
308 my_pString = true;
309
310 strcpy (pNewStates, lineState);
311 delete[] lineState;
312 lineState = pNewStates;
313
314 // copy first part
315 strcpy (pNewString, pString);
316
317 // add spaces
318 char *pAddSpc = pNewString + col - 1;
319 while (tabAmount-- > 0)
320 *pAddSpc++ = SPACE;
321
322 // add original trailing spaces
323 strcpy (pAddSpc, pSTab+1);
324 delete[] pString; // remove old string from memory
325 pString = pNewString;
326 pSTab = pString + col - 1; // point to the first blank
327 //TRACE(("...%d:%s\n", col, pString))
328 }
329 else
330 *pSTab = SPACE;
331
332 }
333 // SCCS ID contains a tab that we don't want to touch
334 else if (*pSTab == '@' && !strncmp(pSTab+1, "(#)", 3))
335 {
336 expand = false;
337 }
338 else if (NonPrintable(*pSTab, deleteChars))
339 {
340 if (quoteChars
341 && (curState == SQuoted
342 || curState == DQuoted)) {
343 char* pOctal = ConvertCharToOctal(*pSTab);
344 char* pTemp = new char[strlen(pString)+strlen(pOctal)+1];
345 if (pOctal == 0 || pTemp == 0)
346 {
347 delete[] pOctal;
348 delete[] pTemp;
349 return;
350 }
351 *pSTab = NULLC;
352 strcpy(pTemp, pString);
353 strcat(pTemp, pOctal);
354 strcat(pTemp, pSTab+1);
355 pSTab = pTemp + (pSTab - pString);
356
357 delete[] pString;
358 pString = pTemp;
359
360 pTemp = new char[strlen(pString)+strlen(pOctal)+1];
361 if (pTemp == 0)
362 {
363 delete[] pOctal;
364 delete[] pTemp;
365 delete[] pString;
366 pString = NULL;
367 return;
368 }
369 strcpy(pTemp, lineState);
370
371 delete[] lineState;
372 lineState = pTemp;
373
374 delete[] pOctal;
375 }
376 else // simply remove the character
377 {
378 int n = 0;
379 while ((pSTab[n] = pSTab[n+1]) != NULLC)
380 n++;
381 }
382 col--;
383 //TRACE(("re-interpret col %d\n", col))
384 continue; // re-interpret character
385 }
386
387 if (skip == 0)
388 oldState = curState;
389 nextCharState(pSTab, curState, skip);
390
391 // Set the saved-state based on whether we're transitioning from
392 // something that's got quotes (which are part of it):
393 lineState[col-1] = (curState == Normal)
394 && ((oldState == DQuoted)
395 || (oldState == SQuoted)
396 || (oldState == Comment))
397 ? oldState
398 : curState;
399
400 // Override the first '#' on a non-continued line to mark a
401 // preprocessor-control.
402 if (*pSTab == POUNDC
403 && !codeOnLine
404 && ispunct(curState))
405 {
406 lineState[col-1] = PreProc;
407 }
408 else if (ispunct(lineState[col-1]))
409 {
410 codeOnLine = true;
411 }
412
413 lineState[col] = NullC;
414
415 pSTab++;
416 }
417
418 // Set up for the next time through this procedure
419 if (curState == Ignore)
420 curState = Normal;
421 if (col == 0
422 || pString[col-1] != ESCAPE)
423 {
424 codeOnLine = false;
425 }
426
427 if (skip == 0
428 && (curState == DQuoted
429 || curState == SQuoted))
430 curState = Normal; // recover from syntax error
431
432 if (last < strlen(pString))
433 {
434 pString[last] = NULLC; // trim trailing blanks
435 lineState[last] = NullC;
436 }
437
438 TRACE((" Expanded (%s)\n", pString));
439 TRACE((" lineState (%s)\n", lineState));
440 TRACE(("%s %d/%d %s\n", last > strlen(pString)+1 ? "FIXME" : "", last, strlen(pString), showCharState(curState)));
441 }
442
443 // ----------------------------------------------------------------------------
444 // This function is used to allocate memory for indentation within function
445 // OutputToOutFile(). Once the memory needed is allocated, it fills the memory
446 // with spaces, or tabs depending upon the fill mode.
447 //
448 // Parameters:
449 // Mode : Defines the fill mode of the memory that it allocate
450 // 1 = tabs only
451 // 2 = spaces only
452 // 3 = both
453 // len : Number of bytes needed to be allocated
454 // spaceIndent:Number of memory locations a tab character take up
455 //
456 // Return Values:
457 // char* : Returns a pointer to the memory/string that was allocated
458 //
TabSpacing(int mode,int col,int len,int spaceIndent)459 char* TabSpacing (int mode, int col, int len, int spaceIndent)
460 {
461 char* pOutTab = NULL;
462 char* pOutSpc = NULL;
463
464 if ((mode & 1) == 1)
465 {
466 int numOfTabs = 0;
467
468 // bypass exception error
469 if (spaceIndent > 0)
470 {
471 numOfTabs = ((len+col) / spaceIndent) - (col / spaceIndent);
472 if (len != 0)
473 len = (len + col) % spaceIndent;
474 }
475
476 pOutTab = new char[numOfTabs + 1];
477 if (pOutTab != NULL)
478 {
479 for (int fillTabs = 0; fillTabs < numOfTabs; fillTabs++)
480 pOutTab[fillTabs] = TAB;
481 pOutTab[numOfTabs] = NULLC;
482 }
483 else
484 return NULL; // memory allocation failed
485
486 // If not in both tab, and space concatenation.
487 if ((mode & 2) == 0)
488 return pOutTab;
489 }//bit 0 set !
490
491 if ((mode & 2) == 2)
492 {
493 if (pOutTab == NULL) //##### normal space allocation !
494 {
495 pOutSpc = new char[len+1];
496 if (pOutSpc != NULL)
497 {
498 for (int fillSpcs = 0; fillSpcs < len; fillSpcs++)
499 pOutSpc[fillSpcs] = SPACE;
500 pOutSpc[len] = NULLC;
501 return pOutSpc; //##### return end product
502 }
503 else
504 return NULL; // memory allocation failed
505 }
506 else // else a mix of spaces & tabs
507 {
508 int numOfSpcs = 0;
509
510 if (spaceIndent > 0)
511 numOfSpcs = len % spaceIndent;
512
513 pOutSpc = new char[numOfSpcs+1];
514 if (pOutSpc != NULL)
515 {
516 for (int fillSpcs = 0; fillSpcs < numOfSpcs; fillSpcs++)
517 pOutSpc[fillSpcs] = SPACE;
518 pOutSpc[numOfSpcs] = NULLC;
519 }
520 else
521 return NULL; // memory allocation failed
522 }
523 }// bit 1 set
524
525 //##### Concatenate tabs & spaces
526 if ( ((mode & 1) == 1) && ((mode & 2) == 2) )
527 {
528 char* pConCat = new char[(strlen (pOutTab) + strlen (pOutSpc) + 1)];
529 // #### Check memory allocation
530 if (pConCat == NULL)
531 {
532 delete[] pOutTab;
533 delete[] pOutSpc;
534 return NULL;
535 }
536 strcpy (pConCat, pOutTab);
537 strcpy (pConCat + strlen (pConCat), pOutSpc);
538 delete[] pOutTab;
539 delete[] pOutSpc;
540 return pConCat;
541 }
542
543 return NULL; //##### illegal mode passed !
544 }
545