1 /****************************************************************************
2 * Copyright (c) 2008-2009,2010 Free Software Foundation, Inc. *
4 * Permission is hereby granted, free of charge, to any person obtaining a *
5 * copy of this software and associated documentation files (the *
6 * "Software"), to deal in the Software without restriction, including *
7 * without limitation the rights to use, copy, modify, merge, publish, *
8 * distribute, distribute with modifications, sublicense, and/or sell *
9 * copies of the Software, and to permit persons to whom the Software is *
10 * furnished to do so, subject to the following conditions: *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
23 * Except as contained in this notice, the name(s) of the above copyright *
24 * holders shall not be used in advertising or otherwise to promote the *
25 * sale, use or other dealings in this Software without prior written *
27 ****************************************************************************/
29 /****************************************************************************
30 * Author: Thomas E. Dickey 2008 *
31 ****************************************************************************/
34 * tabs.c -- set terminal hard-tabstops
38 #include <progs.priv.h>
40 MODULE_ID("$Id: tabs.c,v 1.19 2010/10/23 22:26:01 tom Exp $")
42 static void usage(void) GCC_NORETURN
;
53 do_tabs(int *tab_list
)
59 while ((stop
= *tab_list
++) > 0) {
61 while (last
++ < stop
) {
67 if (stop
<= max_cols
) {
68 tputs(tparm(set_tab
, stop
), 1, putch
);
78 decode_tabs(const char *tab_list
)
80 int *result
= typeCalloc(int, strlen(tab_list
) + (unsigned) max_cols
);
87 while ((ch
= *tab_list
++) != '\0') {
88 if (isdigit(UChar(ch
))) {
91 } else if (ch
== ',') {
92 result
[n
] = value
+ prior
;
93 if (n
> 0 && result
[n
] <= result
[n
- 1]) {
95 "tab-stops are not in increasing order: %d %d\n",
96 value
, result
[n
- 1]);
104 } else if (ch
== '+') {
106 prior
= result
[n
- 1];
113 * If there is only one value, then it is an option such as "-8".
115 if ((n
== 0) && (value
> 0)) {
117 while (n
< max_cols
- 1) {
124 * Add the last value, if any.
126 result
[n
++] = value
+ prior
;
133 print_ruler(int *tab_list
)
139 /* first print a readable ruler */
140 for (n
= 0; n
< max_cols
; n
+= 10) {
141 int ch
= 1 + (n
/ 10);
143 sprintf(buffer
, "----+----%c",
147 printf("%.*s", ((max_cols
- n
) > 10) ? 10 : (max_cols
- n
), buffer
);
151 /* now, print '*' for each stop */
152 for (n
= 0, last
= 0; (tab_list
[n
] > 0) && (last
< max_cols
); ++n
) {
154 while (++last
< stop
) {
155 if (last
<= max_cols
) {
161 if (last
<= max_cols
) {
168 while (++last
<= max_cols
)
174 * Write an '*' on each tabstop, to demonstrate whether it lines up with the
178 write_tabs(int *tab_list
)
182 while ((stop
= *tab_list
++) > 0 && stop
<= max_cols
) {
183 fputs((stop
== 1) ? "*" : "\t*", stdout
);
185 /* also show a tab _past_ the stops */
187 fputs("\t+", stdout
);
192 * Trim leading/trailing blanks, as well as blanks after a comma.
193 * Convert embedded blanks to commas.
196 trimmed_tab_list(const char *source
)
198 char *result
= strdup(source
);
202 for (j
= k
= last
= 0; result
[j
] != 0; ++j
) {
203 ch
= UChar(result
[j
]);
207 } else if (isdigit(last
) || last
== ',') {
210 } else if (ch
== ',') {
214 result
[k
++] = (char) last
;
215 result
[k
++] = (char) ch
;
225 comma_is_needed(const char *source
)
230 unsigned len
= strlen(source
);
232 result
= (source
[len
- 1] != ',');
240 * Add a command-line parameter to the tab-list. It can be blank- or comma-
241 * separated (or a mixture). For simplicity, empty tabs are ignored, e.g.,
244 * are treated the same.
247 add_to_tab_list(char **append
, const char *value
)
249 char *result
= *append
;
250 char *copied
= trimmed_tab_list(value
);
252 if (copied
!= 0 && *copied
!= '\0') {
253 const char *comma
= ",";
254 unsigned need
= 1 + strlen(copied
);
258 else if (!comma_is_needed(*append
))
261 need
+= strlen(comma
);
263 need
+= strlen(*append
);
265 result
= malloc(need
);
269 strcpy(result
, *append
);
272 strcat(result
, comma
);
273 strcat(result
, copied
);
282 * Check for illegal characters in the tab-list.
285 legal_tab_list(const char *program
, const char *tab_list
)
289 if (tab_list
!= 0 && *tab_list
!= '\0') {
290 if (comma_is_needed(tab_list
)) {
292 for (n
= 0; tab_list
[n
] != '\0'; ++n
) {
293 ch
= UChar(tab_list
[n
]);
294 if (!(isdigit(ch
) || ch
== ',' || ch
== '+')) {
296 "%s: unexpected character found '%c'\n",
303 fprintf(stderr
, "%s: trailing comma found '%s'\n", program
, tab_list
);
307 fprintf(stderr
, "%s: no tab-list given\n", program
);
316 static const char *msg
[] =
318 "Usage: tabs [options] [tabstop-list]"
322 ," -8 set tabs to standard interval"
323 ," -a Assembler, IBM S/370, first format"
324 ," -a2 Assembler, IBM S/370, second format"
325 ," -c COBOL, normal format"
326 ," -c2 COBOL compact format"
327 ," -c3 COBOL compact format extended"
328 ," -d debug (show ruler with expected/actual tab positions)"
330 ," -n no-op (do not modify terminal settings)"
333 ," -u UNIVAC 1100 Assembler"
334 ," -T name use terminal type 'name'"
336 ,"A tabstop-list is an ordered list of column numbers, e.g., 1,11,21"
337 ,"or 1,+10,+10 which is the same."
342 for (n
= 0; n
< SIZEOF(msg
); ++n
) {
343 fprintf(stderr
, "%s\n", msg
[n
]);
345 ExitProgram(EXIT_FAILURE
);
349 main(int argc
, char *argv
[])
351 int rc
= EXIT_FAILURE
;
355 NCURSES_CONST
char *term_name
= 0;
356 const char *mar_list
= 0; /* ignored */
358 const char *tab_list
= 0;
360 if ((term_name
= getenv("TERM")) == 0)
361 term_name
= "ansi+tabs";
363 /* cannot use getopt, since some options are two-character */
364 for (n
= 1; n
< argc
; ++n
) {
365 char *option
= argv
[n
];
368 while ((ch
= *++option
) != '\0') {
373 tab_list
= "1,10,16,36,72";
374 /* Assembler, IBM S/370, first format */
377 tab_list
= "1,10,16,40,72";
378 /* Assembler, IBM S/370, second format */
387 tab_list
= "1,8,12,16,20,55";
388 /* COBOL, normal format */
391 tab_list
= "1,6,10,14,49";
392 /* COBOL compact format */
395 tab_list
= "1,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62,67";
396 /* COBOL compact format extended */
402 case 'd': /* ncurses extension */
406 tab_list
= "1,7,11,15,19,23";
409 case 'n': /* ncurses extension */
413 tab_list
= "1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61";
417 tab_list
= "1,10,55";
421 tab_list
= "1,12,20,44";
422 /* UNIVAC 1100 Assembler */
426 if (*++option
!= '\0') {
429 term_name
= argv
[n
++];
431 option
+= ((int) strlen(option
)) - 1;
434 if (isdigit(UChar(*option
))) {
440 option
+= ((int) strlen(option
)) - 1;
446 while ((ch
= *++option
) != '\0') {
452 /* special case of relative stops separated by spaces? */
453 if (option
== argv
[n
] + 1) {
454 tab_list
= add_to_tab_list(&append
, argv
[n
]);
462 if (tab_list
!= (const char *) append
) {
463 /* one of the predefined options was used */
468 tab_list
= add_to_tab_list(&append
, option
);
473 setupterm(term_name
, STDOUT_FILENO
, (int *) 0);
475 max_cols
= (columns
> 0) ? columns
: 80;
477 if (!VALID_STRING(clear_all_tabs
)) {
479 "%s: terminal type '%s' cannot reset tabs\n",
481 } else if (!VALID_STRING(set_tab
)) {
483 "%s: terminal type '%s' cannot set tabs\n",
485 } else if (legal_tab_list(argv
[0], tab_list
)) {
486 int *list
= decode_tabs(tab_list
);
489 tputs(clear_all_tabs
, 1, putch
);
496 printf("tabs %s\n", tab_list
);
503 printf("tabs %s\n", tab_list
);