106bfebdeSXin LI /****************************************************************************
2*21817992SBaptiste Daroussin * Copyright 2020-2022,2023 Thomas E. Dickey *
3e1865124SBaptiste Daroussin * Copyright 2008-2016,2017 Free Software Foundation, Inc. *
406bfebdeSXin LI * *
506bfebdeSXin LI * Permission is hereby granted, free of charge, to any person obtaining a *
606bfebdeSXin LI * copy of this software and associated documentation files (the *
706bfebdeSXin LI * "Software"), to deal in the Software without restriction, including *
806bfebdeSXin LI * without limitation the rights to use, copy, modify, merge, publish, *
906bfebdeSXin LI * distribute, distribute with modifications, sublicense, and/or sell *
1006bfebdeSXin LI * copies of the Software, and to permit persons to whom the Software is *
1106bfebdeSXin LI * furnished to do so, subject to the following conditions: *
1206bfebdeSXin LI * *
1306bfebdeSXin LI * The above copyright notice and this permission notice shall be included *
1406bfebdeSXin LI * in all copies or substantial portions of the Software. *
1506bfebdeSXin LI * *
1606bfebdeSXin LI * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
1706bfebdeSXin LI * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
1806bfebdeSXin LI * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
1906bfebdeSXin LI * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
2006bfebdeSXin LI * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
2106bfebdeSXin LI * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
2206bfebdeSXin LI * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
2306bfebdeSXin LI * *
2406bfebdeSXin LI * Except as contained in this notice, the name(s) of the above copyright *
2506bfebdeSXin LI * holders shall not be used in advertising or otherwise to promote the *
2606bfebdeSXin LI * sale, use or other dealings in this Software without prior written *
2706bfebdeSXin LI * authorization. *
2806bfebdeSXin LI ****************************************************************************/
2906bfebdeSXin LI
3006bfebdeSXin LI /****************************************************************************
3106bfebdeSXin LI * Author: Thomas E. Dickey 2008 *
3206bfebdeSXin LI ****************************************************************************/
3306bfebdeSXin LI
3406bfebdeSXin LI /*
3506bfebdeSXin LI * tabs.c -- set terminal hard-tabstops
3606bfebdeSXin LI */
3706bfebdeSXin LI
3806bfebdeSXin LI #define USE_LIBTINFO
3906bfebdeSXin LI #include <progs.priv.h>
40aae38d10SBaptiste Daroussin #include <tty_settings.h>
4106bfebdeSXin LI
42*21817992SBaptiste Daroussin MODULE_ID("$Id: tabs.c,v 1.53 2023/11/04 20:46:09 tom Exp $")
4306bfebdeSXin LI
44*21817992SBaptiste Daroussin static GCC_NORETURN void usage(void);
4506bfebdeSXin LI
46aae38d10SBaptiste Daroussin const char *_nc_progname;
4706bfebdeSXin LI static int max_cols;
4806bfebdeSXin LI
4973f0a83dSXin LI static void
failed(const char * s)5073f0a83dSXin LI failed(const char *s)
5173f0a83dSXin LI {
5273f0a83dSXin LI perror(s);
5373f0a83dSXin LI ExitProgram(EXIT_FAILURE);
5473f0a83dSXin LI }
5573f0a83dSXin LI
5606bfebdeSXin LI static int
putch(int c)5706bfebdeSXin LI putch(int c)
5806bfebdeSXin LI {
5906bfebdeSXin LI return putchar(c);
6006bfebdeSXin LI }
6106bfebdeSXin LI
62*21817992SBaptiste Daroussin static char *
skip_csi(char * value)63*21817992SBaptiste Daroussin skip_csi(char *value)
64*21817992SBaptiste Daroussin {
65*21817992SBaptiste Daroussin if (UChar(*value) == 0x9b)
66*21817992SBaptiste Daroussin ++value;
67*21817992SBaptiste Daroussin else if (!strncmp(value, "\033[", 2))
68*21817992SBaptiste Daroussin value += 2;
69*21817992SBaptiste Daroussin return value;
70*21817992SBaptiste Daroussin }
71*21817992SBaptiste Daroussin
72*21817992SBaptiste Daroussin /*
73*21817992SBaptiste Daroussin * If the terminal uses ANSI clear_all_tabs, then it is not necessary to first
74*21817992SBaptiste Daroussin * move to the left margin before clearing tabs.
75*21817992SBaptiste Daroussin */
76*21817992SBaptiste Daroussin static bool
ansi_clear_tabs(void)77*21817992SBaptiste Daroussin ansi_clear_tabs(void)
78*21817992SBaptiste Daroussin {
79*21817992SBaptiste Daroussin bool result = FALSE;
80*21817992SBaptiste Daroussin if (VALID_STRING(clear_all_tabs)) {
81*21817992SBaptiste Daroussin char *param = skip_csi(clear_all_tabs);
82*21817992SBaptiste Daroussin if (!strcmp(param, "3g"))
83*21817992SBaptiste Daroussin result = TRUE;
84*21817992SBaptiste Daroussin }
85*21817992SBaptiste Daroussin return result;
86*21817992SBaptiste Daroussin }
87*21817992SBaptiste Daroussin
8806bfebdeSXin LI static void
do_tabs(int * tab_list)8906bfebdeSXin LI do_tabs(int *tab_list)
9006bfebdeSXin LI {
9106bfebdeSXin LI int last = 1;
9206bfebdeSXin LI int stop;
93*21817992SBaptiste Daroussin bool first = TRUE;
9406bfebdeSXin LI
9506bfebdeSXin LI while ((stop = *tab_list++) > 0) {
96*21817992SBaptiste Daroussin if (first) {
97*21817992SBaptiste Daroussin first = FALSE;
98*21817992SBaptiste Daroussin putchar('\r');
99*21817992SBaptiste Daroussin }
10006bfebdeSXin LI if (last < stop) {
10106bfebdeSXin LI while (last++ < stop) {
10206bfebdeSXin LI if (last > max_cols)
10306bfebdeSXin LI break;
10406bfebdeSXin LI putchar(' ');
10506bfebdeSXin LI }
10606bfebdeSXin LI }
10706bfebdeSXin LI if (stop <= max_cols) {
108*21817992SBaptiste Daroussin tputs(set_tab, 1, putch);
10906bfebdeSXin LI last = stop;
11006bfebdeSXin LI } else {
11106bfebdeSXin LI break;
11206bfebdeSXin LI }
11306bfebdeSXin LI }
114aae38d10SBaptiste Daroussin putchar('\r');
11506bfebdeSXin LI }
11606bfebdeSXin LI
117*21817992SBaptiste Daroussin /*
118*21817992SBaptiste Daroussin * Decode a list of tab-stops from a string, returning an array of integers.
119*21817992SBaptiste Daroussin * If the margin is positive (because the terminal does not support margins),
120*21817992SBaptiste Daroussin * work around this by adding the margin to the decoded values.
121*21817992SBaptiste Daroussin */
12206bfebdeSXin LI static int *
decode_tabs(const char * tab_list,int margin)123*21817992SBaptiste Daroussin decode_tabs(const char *tab_list, int margin)
12406bfebdeSXin LI {
12506bfebdeSXin LI int *result = typeCalloc(int, strlen(tab_list) + (unsigned) max_cols);
12606bfebdeSXin LI int n = 0;
12706bfebdeSXin LI int value = 0;
12806bfebdeSXin LI int prior = 0;
12906bfebdeSXin LI int ch;
13006bfebdeSXin LI
131*21817992SBaptiste Daroussin if (result == NULL)
13273f0a83dSXin LI failed("decode_tabs");
13373f0a83dSXin LI
134*21817992SBaptiste Daroussin if (margin < 0)
135*21817992SBaptiste Daroussin margin = 0;
136*21817992SBaptiste Daroussin
13706bfebdeSXin LI while ((ch = *tab_list++) != '\0') {
13806bfebdeSXin LI if (isdigit(UChar(ch))) {
13906bfebdeSXin LI value *= 10;
14006bfebdeSXin LI value += (ch - '0');
141*21817992SBaptiste Daroussin if (value > max_cols)
142*21817992SBaptiste Daroussin value = max_cols;
14306bfebdeSXin LI } else if (ch == ',') {
144*21817992SBaptiste Daroussin result[n] = value + prior + margin;
14506bfebdeSXin LI if (n > 0 && result[n] <= result[n - 1]) {
14606bfebdeSXin LI fprintf(stderr,
14773f0a83dSXin LI "%s: tab-stops are not in increasing order: %d %d\n",
148aae38d10SBaptiste Daroussin _nc_progname, value, result[n - 1]);
14906bfebdeSXin LI free(result);
15006bfebdeSXin LI result = 0;
15106bfebdeSXin LI break;
15206bfebdeSXin LI }
15306bfebdeSXin LI ++n;
15406bfebdeSXin LI value = 0;
15506bfebdeSXin LI prior = 0;
15606bfebdeSXin LI } else if (ch == '+') {
15706bfebdeSXin LI if (n)
15806bfebdeSXin LI prior = result[n - 1];
15906bfebdeSXin LI }
16006bfebdeSXin LI }
16106bfebdeSXin LI
16206bfebdeSXin LI if (result != 0) {
16306bfebdeSXin LI /*
16406bfebdeSXin LI * If there is only one value, then it is an option such as "-8".
16506bfebdeSXin LI */
16606bfebdeSXin LI if ((n == 0) && (value > 0)) {
16706bfebdeSXin LI int step = value;
16873f0a83dSXin LI value = 1;
16906bfebdeSXin LI while (n < max_cols - 1) {
170*21817992SBaptiste Daroussin result[n++] = value + margin;
17106bfebdeSXin LI value += step;
17206bfebdeSXin LI }
17306bfebdeSXin LI }
17406bfebdeSXin LI
17506bfebdeSXin LI /*
17606bfebdeSXin LI * Add the last value, if any.
17706bfebdeSXin LI */
178*21817992SBaptiste Daroussin result[n++] = value + prior + margin;
17906bfebdeSXin LI result[n] = 0;
18006bfebdeSXin LI }
18173f0a83dSXin LI
18206bfebdeSXin LI return result;
18306bfebdeSXin LI }
18406bfebdeSXin LI
18506bfebdeSXin LI static void
print_ruler(const int * const tab_list,const char * new_line)186*21817992SBaptiste Daroussin print_ruler(const int *const tab_list, const char *new_line)
18706bfebdeSXin LI {
18806bfebdeSXin LI int last = 0;
18906bfebdeSXin LI int n;
19006bfebdeSXin LI
19106bfebdeSXin LI /* first print a readable ruler */
19206bfebdeSXin LI for (n = 0; n < max_cols; n += 10) {
19306bfebdeSXin LI int ch = 1 + (n / 10);
19406bfebdeSXin LI char buffer[20];
19573f0a83dSXin LI _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
19673f0a83dSXin LI "----+----%c",
19706bfebdeSXin LI ((ch < 10)
19806bfebdeSXin LI ? (ch + '0')
19906bfebdeSXin LI : (ch + 'A' - 10)));
20006bfebdeSXin LI printf("%.*s", ((max_cols - n) > 10) ? 10 : (max_cols - n), buffer);
20106bfebdeSXin LI }
202*21817992SBaptiste Daroussin printf("%s", new_line);
20306bfebdeSXin LI
20406bfebdeSXin LI /* now, print '*' for each stop */
20506bfebdeSXin LI for (n = 0, last = 0; (tab_list[n] > 0) && (last < max_cols); ++n) {
206*21817992SBaptiste Daroussin int stop = tab_list[n];
207*21817992SBaptiste Daroussin
20806bfebdeSXin LI while (++last < stop) {
20906bfebdeSXin LI if (last <= max_cols) {
21006bfebdeSXin LI putchar('-');
21106bfebdeSXin LI } else {
21206bfebdeSXin LI break;
21306bfebdeSXin LI }
21406bfebdeSXin LI }
21506bfebdeSXin LI if (last <= max_cols) {
21606bfebdeSXin LI putchar('*');
21706bfebdeSXin LI last = stop;
21806bfebdeSXin LI } else {
21906bfebdeSXin LI break;
22006bfebdeSXin LI }
22106bfebdeSXin LI }
22206bfebdeSXin LI while (++last <= max_cols)
22306bfebdeSXin LI putchar('-');
224*21817992SBaptiste Daroussin printf("%s", new_line);
22506bfebdeSXin LI }
22606bfebdeSXin LI
22706bfebdeSXin LI /*
22806bfebdeSXin LI * Write an '*' on each tabstop, to demonstrate whether it lines up with the
22906bfebdeSXin LI * ruler.
23006bfebdeSXin LI */
23106bfebdeSXin LI static void
write_tabs(int * tab_list,const char * new_line)232*21817992SBaptiste Daroussin write_tabs(int *tab_list, const char *new_line)
23306bfebdeSXin LI {
23406bfebdeSXin LI int stop;
23506bfebdeSXin LI
23606bfebdeSXin LI while ((stop = *tab_list++) > 0 && stop <= max_cols) {
23706bfebdeSXin LI fputs((stop == 1) ? "*" : "\t*", stdout);
23806bfebdeSXin LI };
23906bfebdeSXin LI /* also show a tab _past_ the stops */
24006bfebdeSXin LI if (stop < max_cols)
24106bfebdeSXin LI fputs("\t+", stdout);
242*21817992SBaptiste Daroussin fputs(new_line, stdout);
24306bfebdeSXin LI }
24406bfebdeSXin LI
24506bfebdeSXin LI /*
24606bfebdeSXin LI * Trim leading/trailing blanks, as well as blanks after a comma.
24706bfebdeSXin LI * Convert embedded blanks to commas.
24806bfebdeSXin LI */
24906bfebdeSXin LI static char *
trimmed_tab_list(const char * source)25006bfebdeSXin LI trimmed_tab_list(const char *source)
25106bfebdeSXin LI {
25206bfebdeSXin LI char *result = strdup(source);
25306bfebdeSXin LI if (result != 0) {
254*21817992SBaptiste Daroussin int j, k, last;
255*21817992SBaptiste Daroussin
25606bfebdeSXin LI for (j = k = last = 0; result[j] != 0; ++j) {
257*21817992SBaptiste Daroussin int ch = UChar(result[j]);
25806bfebdeSXin LI if (isspace(ch)) {
25906bfebdeSXin LI if (last == '\0') {
26006bfebdeSXin LI continue;
26106bfebdeSXin LI } else if (isdigit(last) || last == ',') {
26206bfebdeSXin LI ch = ',';
26306bfebdeSXin LI }
26406bfebdeSXin LI } else if (ch == ',') {
26506bfebdeSXin LI ;
26606bfebdeSXin LI } else {
26706bfebdeSXin LI if (last == ',')
26806bfebdeSXin LI result[k++] = (char) last;
26906bfebdeSXin LI result[k++] = (char) ch;
27006bfebdeSXin LI }
27106bfebdeSXin LI last = ch;
27206bfebdeSXin LI }
27306bfebdeSXin LI result[k] = '\0';
27406bfebdeSXin LI }
27506bfebdeSXin LI return result;
27606bfebdeSXin LI }
27706bfebdeSXin LI
27806bfebdeSXin LI static bool
comma_is_needed(const char * source)27906bfebdeSXin LI comma_is_needed(const char *source)
28006bfebdeSXin LI {
28106bfebdeSXin LI bool result = FALSE;
28206bfebdeSXin LI
28306bfebdeSXin LI if (source != 0) {
28473f0a83dSXin LI size_t len = strlen(source);
28506bfebdeSXin LI if (len != 0)
28606bfebdeSXin LI result = (source[len - 1] != ',');
28706bfebdeSXin LI } else {
28806bfebdeSXin LI result = FALSE;
28906bfebdeSXin LI }
29006bfebdeSXin LI return result;
29106bfebdeSXin LI }
29206bfebdeSXin LI
29306bfebdeSXin LI /*
29406bfebdeSXin LI * Add a command-line parameter to the tab-list. It can be blank- or comma-
29506bfebdeSXin LI * separated (or a mixture). For simplicity, empty tabs are ignored, e.g.,
29606bfebdeSXin LI * tabs 1,,6,11
29706bfebdeSXin LI * tabs 1,6,11
29806bfebdeSXin LI * are treated the same.
29906bfebdeSXin LI */
30006bfebdeSXin LI static const char *
add_to_tab_list(char ** append,const char * value)30106bfebdeSXin LI add_to_tab_list(char **append, const char *value)
30206bfebdeSXin LI {
30306bfebdeSXin LI char *result = *append;
30406bfebdeSXin LI char *copied = trimmed_tab_list(value);
30506bfebdeSXin LI
30606bfebdeSXin LI if (copied != 0 && *copied != '\0') {
30706bfebdeSXin LI const char *comma = ",";
30873f0a83dSXin LI size_t need = 1 + strlen(copied);
30906bfebdeSXin LI
31006bfebdeSXin LI if (*copied == ',')
31106bfebdeSXin LI comma = "";
31206bfebdeSXin LI else if (!comma_is_needed(*append))
31306bfebdeSXin LI comma = "";
31406bfebdeSXin LI
31506bfebdeSXin LI need += strlen(comma);
31606bfebdeSXin LI if (*append != 0)
31706bfebdeSXin LI need += strlen(*append);
31806bfebdeSXin LI
31906bfebdeSXin LI result = malloc(need);
32073f0a83dSXin LI if (result == 0)
32173f0a83dSXin LI failed("add_to_tab_list");
32273f0a83dSXin LI
32306bfebdeSXin LI *result = '\0';
32406bfebdeSXin LI if (*append != 0) {
32573f0a83dSXin LI _nc_STRCPY(result, *append, need);
32606bfebdeSXin LI free(*append);
32706bfebdeSXin LI }
32873f0a83dSXin LI _nc_STRCAT(result, comma, need);
32973f0a83dSXin LI _nc_STRCAT(result, copied, need);
33006bfebdeSXin LI
33106bfebdeSXin LI *append = result;
33206bfebdeSXin LI }
333aae38d10SBaptiste Daroussin free(copied);
33406bfebdeSXin LI return result;
33506bfebdeSXin LI }
33606bfebdeSXin LI
33706bfebdeSXin LI /*
338*21817992SBaptiste Daroussin * If the terminal supports it, (re)set the left margin and return true.
339*21817992SBaptiste Daroussin * Otherwise, return false.
340*21817992SBaptiste Daroussin */
341*21817992SBaptiste Daroussin static bool
do_set_margin(int margin,bool no_op)342*21817992SBaptiste Daroussin do_set_margin(int margin, bool no_op)
343*21817992SBaptiste Daroussin {
344*21817992SBaptiste Daroussin bool result = FALSE;
345*21817992SBaptiste Daroussin
346*21817992SBaptiste Daroussin if (margin == 0) { /* 0 is special case for resetting */
347*21817992SBaptiste Daroussin if (VALID_STRING(clear_margins)) {
348*21817992SBaptiste Daroussin result = TRUE;
349*21817992SBaptiste Daroussin if (!no_op)
350*21817992SBaptiste Daroussin tputs(clear_margins, 1, putch);
351*21817992SBaptiste Daroussin }
352*21817992SBaptiste Daroussin } else if (margin-- < 0) { /* margin will be 0-based from here on */
353*21817992SBaptiste Daroussin result = TRUE;
354*21817992SBaptiste Daroussin } else if (VALID_STRING(set_left_margin)) {
355*21817992SBaptiste Daroussin result = TRUE;
356*21817992SBaptiste Daroussin if (!no_op) {
357*21817992SBaptiste Daroussin /*
358*21817992SBaptiste Daroussin * assuming we're on the first column of the line, move the cursor
359*21817992SBaptiste Daroussin * to the column at which we will set a margin.
360*21817992SBaptiste Daroussin */
361*21817992SBaptiste Daroussin if (VALID_STRING(column_address)) {
362*21817992SBaptiste Daroussin tputs(TIPARM_1(column_address, margin), 1, putch);
363*21817992SBaptiste Daroussin } else if (margin >= 1) {
364*21817992SBaptiste Daroussin if (VALID_STRING(parm_right_cursor)) {
365*21817992SBaptiste Daroussin tputs(TIPARM_1(parm_right_cursor, margin), 1, putch);
366*21817992SBaptiste Daroussin } else {
367*21817992SBaptiste Daroussin while (margin-- > 0)
368*21817992SBaptiste Daroussin putch(' ');
369*21817992SBaptiste Daroussin }
370*21817992SBaptiste Daroussin }
371*21817992SBaptiste Daroussin tputs(set_left_margin, 1, putch);
372*21817992SBaptiste Daroussin }
373*21817992SBaptiste Daroussin }
374*21817992SBaptiste Daroussin #if defined(set_left_margin_parm) && defined(set_right_margin_parm)
375*21817992SBaptiste Daroussin else if (VALID_STRING(set_left_margin_parm)) {
376*21817992SBaptiste Daroussin result = TRUE;
377*21817992SBaptiste Daroussin if (!no_op) {
378*21817992SBaptiste Daroussin if (VALID_STRING(set_right_margin_parm)) {
379*21817992SBaptiste Daroussin tputs(TIPARM_1(set_left_margin_parm, margin), 1, putch);
380*21817992SBaptiste Daroussin } else {
381*21817992SBaptiste Daroussin tputs(TIPARM_2(set_left_margin_parm, margin, max_cols), 1, putch);
382*21817992SBaptiste Daroussin }
383*21817992SBaptiste Daroussin }
384*21817992SBaptiste Daroussin }
385*21817992SBaptiste Daroussin #endif
386*21817992SBaptiste Daroussin #if defined(set_lr_margin)
387*21817992SBaptiste Daroussin else if (VALID_STRING(set_lr_margin)) {
388*21817992SBaptiste Daroussin result = TRUE;
389*21817992SBaptiste Daroussin if (!no_op) {
390*21817992SBaptiste Daroussin tputs(TIPARM_2(set_lr_margin, margin, max_cols), 1, putch);
391*21817992SBaptiste Daroussin }
392*21817992SBaptiste Daroussin }
393*21817992SBaptiste Daroussin #endif
394*21817992SBaptiste Daroussin return result;
395*21817992SBaptiste Daroussin }
396*21817992SBaptiste Daroussin
397*21817992SBaptiste Daroussin /*
39806bfebdeSXin LI * Check for illegal characters in the tab-list.
39906bfebdeSXin LI */
40006bfebdeSXin LI static bool
legal_tab_list(const char * tab_list)40173f0a83dSXin LI legal_tab_list(const char *tab_list)
40206bfebdeSXin LI {
40306bfebdeSXin LI bool result = TRUE;
40406bfebdeSXin LI
40506bfebdeSXin LI if (tab_list != 0 && *tab_list != '\0') {
40606bfebdeSXin LI if (comma_is_needed(tab_list)) {
407*21817992SBaptiste Daroussin int n;
408*21817992SBaptiste Daroussin
40906bfebdeSXin LI for (n = 0; tab_list[n] != '\0'; ++n) {
410*21817992SBaptiste Daroussin int ch = UChar(tab_list[n]);
411*21817992SBaptiste Daroussin
41206bfebdeSXin LI if (!(isdigit(ch) || ch == ',' || ch == '+')) {
41306bfebdeSXin LI fprintf(stderr,
41406bfebdeSXin LI "%s: unexpected character found '%c'\n",
415aae38d10SBaptiste Daroussin _nc_progname, ch);
41606bfebdeSXin LI result = FALSE;
41706bfebdeSXin LI break;
41806bfebdeSXin LI }
41906bfebdeSXin LI }
42006bfebdeSXin LI } else {
421aae38d10SBaptiste Daroussin fprintf(stderr, "%s: trailing comma found '%s'\n", _nc_progname, tab_list);
42206bfebdeSXin LI result = FALSE;
42306bfebdeSXin LI }
42406bfebdeSXin LI } else {
425*21817992SBaptiste Daroussin /* if no list given, default to "tabs -8" */
42606bfebdeSXin LI }
42706bfebdeSXin LI return result;
42806bfebdeSXin LI }
42906bfebdeSXin LI
43073f0a83dSXin LI static char *
skip_list(char * value)43173f0a83dSXin LI skip_list(char *value)
43273f0a83dSXin LI {
43373f0a83dSXin LI while (*value != '\0' &&
43473f0a83dSXin LI (isdigit(UChar(*value)) ||
43573f0a83dSXin LI isspace(UChar(*value)) ||
43673f0a83dSXin LI strchr("+,", UChar(*value)) != 0)) {
43773f0a83dSXin LI ++value;
43873f0a83dSXin LI }
43973f0a83dSXin LI return value;
44073f0a83dSXin LI }
44173f0a83dSXin LI
44206bfebdeSXin LI static void
usage(void)44306bfebdeSXin LI usage(void)
44406bfebdeSXin LI {
445aae38d10SBaptiste Daroussin #define DATA(s) s "\n"
446aae38d10SBaptiste Daroussin static const char msg[] =
44706bfebdeSXin LI {
448aae38d10SBaptiste Daroussin DATA("Usage: tabs [options] [tabstop-list]")
449aae38d10SBaptiste Daroussin DATA("")
450aae38d10SBaptiste Daroussin DATA("Options:")
451aae38d10SBaptiste Daroussin DATA(" -0 reset tabs")
452aae38d10SBaptiste Daroussin DATA(" -8 set tabs to standard interval")
453aae38d10SBaptiste Daroussin DATA(" -a Assembler, IBM S/370, first format")
454aae38d10SBaptiste Daroussin DATA(" -a2 Assembler, IBM S/370, second format")
455aae38d10SBaptiste Daroussin DATA(" -c COBOL, normal format")
456aae38d10SBaptiste Daroussin DATA(" -c2 COBOL compact format")
457aae38d10SBaptiste Daroussin DATA(" -c3 COBOL compact format extended")
458aae38d10SBaptiste Daroussin DATA(" -d debug (show ruler with expected/actual tab positions)")
459aae38d10SBaptiste Daroussin DATA(" -f FORTRAN")
460aae38d10SBaptiste Daroussin DATA(" -n no-op (do not modify terminal settings)")
461aae38d10SBaptiste Daroussin DATA(" -p PL/I")
462aae38d10SBaptiste Daroussin DATA(" -s SNOBOL")
463aae38d10SBaptiste Daroussin DATA(" -u UNIVAC 1100 Assembler")
464aae38d10SBaptiste Daroussin DATA(" -T name use terminal type 'name'")
465aae38d10SBaptiste Daroussin DATA(" -V print version")
466aae38d10SBaptiste Daroussin DATA("")
467aae38d10SBaptiste Daroussin DATA("A tabstop-list is an ordered list of column numbers, e.g., 1,11,21")
468aae38d10SBaptiste Daroussin DATA("or 1,+10,+10 which is the same.")
46906bfebdeSXin LI };
470aae38d10SBaptiste Daroussin #undef DATA
47106bfebdeSXin LI
47206bfebdeSXin LI fflush(stdout);
473aae38d10SBaptiste Daroussin fputs(msg, stderr);
47406bfebdeSXin LI ExitProgram(EXIT_FAILURE);
47506bfebdeSXin LI }
47606bfebdeSXin LI
47706bfebdeSXin LI int
main(int argc,char * argv[])47806bfebdeSXin LI main(int argc, char *argv[])
47906bfebdeSXin LI {
48006bfebdeSXin LI int rc = EXIT_FAILURE;
48106bfebdeSXin LI bool debug = FALSE;
48206bfebdeSXin LI bool no_op = FALSE;
483*21817992SBaptiste Daroussin bool change_tty = FALSE;
48406bfebdeSXin LI int n, ch;
48506bfebdeSXin LI NCURSES_CONST char *term_name = 0;
48606bfebdeSXin LI char *append = 0;
48706bfebdeSXin LI const char *tab_list = 0;
488*21817992SBaptiste Daroussin const char *new_line = "\n";
489*21817992SBaptiste Daroussin int margin = -1;
490aae38d10SBaptiste Daroussin TTY tty_settings;
491aae38d10SBaptiste Daroussin int fd;
49206bfebdeSXin LI
493aae38d10SBaptiste Daroussin _nc_progname = _nc_rootname(argv[0]);
494aae38d10SBaptiste Daroussin
49506bfebdeSXin LI if ((term_name = getenv("TERM")) == 0)
49606bfebdeSXin LI term_name = "ansi+tabs";
49706bfebdeSXin LI
49806bfebdeSXin LI /* cannot use getopt, since some options are two-character */
49906bfebdeSXin LI for (n = 1; n < argc; ++n) {
50006bfebdeSXin LI char *option = argv[n];
50106bfebdeSXin LI switch (option[0]) {
50206bfebdeSXin LI case '-':
50306bfebdeSXin LI while ((ch = *++option) != '\0') {
50406bfebdeSXin LI switch (ch) {
50506bfebdeSXin LI case 'a':
50673f0a83dSXin LI switch (*++option) {
50773f0a83dSXin LI default:
50806bfebdeSXin LI case '\0':
50906bfebdeSXin LI tab_list = "1,10,16,36,72";
51073f0a83dSXin LI option--;
51106bfebdeSXin LI /* Assembler, IBM S/370, first format */
51206bfebdeSXin LI break;
51306bfebdeSXin LI case '2':
51406bfebdeSXin LI tab_list = "1,10,16,40,72";
51506bfebdeSXin LI /* Assembler, IBM S/370, second format */
51606bfebdeSXin LI break;
51706bfebdeSXin LI }
51806bfebdeSXin LI break;
51906bfebdeSXin LI case 'c':
52073f0a83dSXin LI switch (*++option) {
52173f0a83dSXin LI default:
52206bfebdeSXin LI case '\0':
52306bfebdeSXin LI tab_list = "1,8,12,16,20,55";
52473f0a83dSXin LI option--;
52506bfebdeSXin LI /* COBOL, normal format */
52606bfebdeSXin LI break;
52706bfebdeSXin LI case '2':
52806bfebdeSXin LI tab_list = "1,6,10,14,49";
52906bfebdeSXin LI /* COBOL compact format */
53006bfebdeSXin LI break;
53106bfebdeSXin LI case '3':
53206bfebdeSXin LI tab_list = "1,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62,67";
53306bfebdeSXin LI /* COBOL compact format extended */
53406bfebdeSXin LI break;
53506bfebdeSXin LI }
53606bfebdeSXin LI break;
53706bfebdeSXin LI case 'd': /* ncurses extension */
53806bfebdeSXin LI debug = TRUE;
53906bfebdeSXin LI break;
54006bfebdeSXin LI case 'f':
54106bfebdeSXin LI tab_list = "1,7,11,15,19,23";
54206bfebdeSXin LI /* FORTRAN */
54306bfebdeSXin LI break;
54406bfebdeSXin LI case 'n': /* ncurses extension */
54506bfebdeSXin LI no_op = TRUE;
54606bfebdeSXin LI break;
54706bfebdeSXin LI case 'p':
54806bfebdeSXin LI tab_list = "1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61";
54906bfebdeSXin LI /* PL/I */
55006bfebdeSXin LI break;
55106bfebdeSXin LI case 's':
55206bfebdeSXin LI tab_list = "1,10,55";
55306bfebdeSXin LI /* SNOBOL */
55406bfebdeSXin LI break;
55506bfebdeSXin LI case 'u':
55606bfebdeSXin LI tab_list = "1,12,20,44";
55706bfebdeSXin LI /* UNIVAC 1100 Assembler */
55806bfebdeSXin LI break;
55906bfebdeSXin LI case 'T':
56006bfebdeSXin LI ++n;
56106bfebdeSXin LI if (*++option != '\0') {
56206bfebdeSXin LI term_name = option;
56306bfebdeSXin LI } else {
564aae38d10SBaptiste Daroussin term_name = argv[n];
56573f0a83dSXin LI option--;
56606bfebdeSXin LI }
56706bfebdeSXin LI option += ((int) strlen(option)) - 1;
56806bfebdeSXin LI continue;
56973f0a83dSXin LI case 'V':
57073f0a83dSXin LI puts(curses_version());
57173f0a83dSXin LI ExitProgram(EXIT_SUCCESS);
57206bfebdeSXin LI default:
57306bfebdeSXin LI if (isdigit(UChar(*option))) {
57473f0a83dSXin LI char *copy = strdup(option);
57573f0a83dSXin LI *skip_list(copy) = '\0';
57673f0a83dSXin LI tab_list = copy;
57773f0a83dSXin LI option = skip_list(option) - 1;
57806bfebdeSXin LI } else {
57906bfebdeSXin LI usage();
58006bfebdeSXin LI }
58106bfebdeSXin LI break;
58206bfebdeSXin LI }
58306bfebdeSXin LI }
58406bfebdeSXin LI break;
58506bfebdeSXin LI case '+':
586*21817992SBaptiste Daroussin if ((ch = *++option) != '\0') {
587*21817992SBaptiste Daroussin int digits = 0;
588*21817992SBaptiste Daroussin int number = 0;
589*21817992SBaptiste Daroussin
59006bfebdeSXin LI switch (ch) {
59106bfebdeSXin LI case 'm':
59273f0a83dSXin LI /*
59373f0a83dSXin LI * The "+mXXX" option is unimplemented because only the long-obsolete
59473f0a83dSXin LI * att510d implements smgl, which is needed to support
59573f0a83dSXin LI * this option.
59673f0a83dSXin LI */
597*21817992SBaptiste Daroussin while ((ch = *++option) != '\0') {
598*21817992SBaptiste Daroussin if (isdigit(UChar(ch))) {
599*21817992SBaptiste Daroussin ++digits;
600*21817992SBaptiste Daroussin number = number * 10 + (ch - '0');
601*21817992SBaptiste Daroussin } else {
602*21817992SBaptiste Daroussin usage();
603*21817992SBaptiste Daroussin }
604*21817992SBaptiste Daroussin }
605*21817992SBaptiste Daroussin if (digits == 0)
606*21817992SBaptiste Daroussin number = 10;
607*21817992SBaptiste Daroussin margin = number;
60806bfebdeSXin LI break;
60906bfebdeSXin LI default:
61006bfebdeSXin LI /* special case of relative stops separated by spaces? */
61106bfebdeSXin LI if (option == argv[n] + 1) {
61206bfebdeSXin LI tab_list = add_to_tab_list(&append, argv[n]);
61306bfebdeSXin LI }
61406bfebdeSXin LI break;
61506bfebdeSXin LI }
61606bfebdeSXin LI }
61706bfebdeSXin LI break;
61806bfebdeSXin LI default:
61906bfebdeSXin LI if (append != 0) {
62006bfebdeSXin LI if (tab_list != (const char *) append) {
62106bfebdeSXin LI /* one of the predefined options was used */
62206bfebdeSXin LI free(append);
62306bfebdeSXin LI append = 0;
62406bfebdeSXin LI }
62506bfebdeSXin LI }
62606bfebdeSXin LI tab_list = add_to_tab_list(&append, option);
62706bfebdeSXin LI break;
62806bfebdeSXin LI }
62906bfebdeSXin LI }
63006bfebdeSXin LI
631*21817992SBaptiste Daroussin fd = save_tty_settings(&tty_settings, FALSE);
632*21817992SBaptiste Daroussin
633aae38d10SBaptiste Daroussin setupterm(term_name, fd, (int *) 0);
63406bfebdeSXin LI
63506bfebdeSXin LI max_cols = (columns > 0) ? columns : 80;
636*21817992SBaptiste Daroussin if (margin > 0)
637*21817992SBaptiste Daroussin max_cols -= margin;
63806bfebdeSXin LI
63906bfebdeSXin LI if (!VALID_STRING(clear_all_tabs)) {
64006bfebdeSXin LI fprintf(stderr,
64106bfebdeSXin LI "%s: terminal type '%s' cannot reset tabs\n",
642aae38d10SBaptiste Daroussin _nc_progname, term_name);
64306bfebdeSXin LI } else if (!VALID_STRING(set_tab)) {
64406bfebdeSXin LI fprintf(stderr,
64506bfebdeSXin LI "%s: terminal type '%s' cannot set tabs\n",
646aae38d10SBaptiste Daroussin _nc_progname, term_name);
64773f0a83dSXin LI } else if (legal_tab_list(tab_list)) {
648*21817992SBaptiste Daroussin int *list;
64906bfebdeSXin LI
650*21817992SBaptiste Daroussin if (tab_list == NULL)
651*21817992SBaptiste Daroussin tab_list = add_to_tab_list(&append, "8");
652*21817992SBaptiste Daroussin
653*21817992SBaptiste Daroussin if (!no_op) {
654*21817992SBaptiste Daroussin #if defined(TERMIOS) && defined(OCRNL)
655*21817992SBaptiste Daroussin /* set tty modes to -ocrnl to allow \r */
656*21817992SBaptiste Daroussin if (isatty(STDOUT_FILENO)) {
657*21817992SBaptiste Daroussin TTY new_settings = tty_settings;
658*21817992SBaptiste Daroussin new_settings.c_oflag &= (unsigned) ~OCRNL;
659*21817992SBaptiste Daroussin update_tty_settings(&tty_settings, &new_settings);
660*21817992SBaptiste Daroussin change_tty = TRUE;
661*21817992SBaptiste Daroussin new_line = "\r\n";
662*21817992SBaptiste Daroussin }
663*21817992SBaptiste Daroussin #endif
664*21817992SBaptiste Daroussin
665*21817992SBaptiste Daroussin if (!ansi_clear_tabs())
666*21817992SBaptiste Daroussin putch('\r');
66706bfebdeSXin LI tputs(clear_all_tabs, 1, putch);
668*21817992SBaptiste Daroussin }
669*21817992SBaptiste Daroussin
670*21817992SBaptiste Daroussin if (margin >= 0) {
671*21817992SBaptiste Daroussin putch('\r');
672*21817992SBaptiste Daroussin if (margin > 0) {
673*21817992SBaptiste Daroussin /* reset existing margin before setting margin, to reduce
674*21817992SBaptiste Daroussin * problems moving left of the current margin.
675*21817992SBaptiste Daroussin */
676*21817992SBaptiste Daroussin if (do_set_margin(0, no_op))
677*21817992SBaptiste Daroussin putch('\r');
678*21817992SBaptiste Daroussin }
679*21817992SBaptiste Daroussin if (do_set_margin(margin, no_op))
680*21817992SBaptiste Daroussin margin = -1;
681*21817992SBaptiste Daroussin }
682*21817992SBaptiste Daroussin
683*21817992SBaptiste Daroussin list = decode_tabs(tab_list, margin);
68406bfebdeSXin LI
68506bfebdeSXin LI if (list != 0) {
68606bfebdeSXin LI if (!no_op)
68706bfebdeSXin LI do_tabs(list);
68806bfebdeSXin LI if (debug) {
68906bfebdeSXin LI fflush(stderr);
690*21817992SBaptiste Daroussin printf("tabs %s%s", tab_list, new_line);
691*21817992SBaptiste Daroussin print_ruler(list, new_line);
692*21817992SBaptiste Daroussin write_tabs(list, new_line);
69306bfebdeSXin LI }
69406bfebdeSXin LI free(list);
69506bfebdeSXin LI } else if (debug) {
69606bfebdeSXin LI fflush(stderr);
697*21817992SBaptiste Daroussin printf("tabs %s%s", tab_list, new_line);
698*21817992SBaptiste Daroussin }
699*21817992SBaptiste Daroussin if (!no_op) {
700*21817992SBaptiste Daroussin if (change_tty) {
701*21817992SBaptiste Daroussin restore_tty_settings();
702*21817992SBaptiste Daroussin }
70306bfebdeSXin LI }
70406bfebdeSXin LI rc = EXIT_SUCCESS;
70506bfebdeSXin LI }
70606bfebdeSXin LI if (append != 0)
70706bfebdeSXin LI free(append);
70806bfebdeSXin LI ExitProgram(rc);
70906bfebdeSXin LI }
710