logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

paste.c (5980B)


  1. /*-
  2. * SPDX-License-Identifier: BSD-3-Clause
  3. *
  4. * Copyright (c) 1989, 1993
  5. * The Regents of the University of California. All rights reserved.
  6. *
  7. * This code is derived from software contributed to Berkeley by
  8. * Adam S. Moskowitz of Menlo Consulting.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in the
  17. * documentation and/or other materials provided with the distribution.
  18. * 3. Neither the name of the University nor the names of its contributors
  19. * may be used to endorse or promote products derived from this software
  20. * without specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  23. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  26. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  27. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  28. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  31. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32. * SUCH DAMAGE.
  33. */
  34. #define _POSIX_C_SOURCE 200809L
  35. #include "../lib/err.h"
  36. #include <errno.h>
  37. #include <limits.h>
  38. #include <locale.h>
  39. #include <stdint.h> // SIZE_MAX
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42. #include <string.h>
  43. #include <sys/types.h>
  44. #include <unistd.h>
  45. #include <wchar.h>
  46. static wchar_t *delim;
  47. static int delimcnt;
  48. static wint_t linedelim = L'\n';
  49. static wchar_t tab[] = L"\t";
  50. const char *argv0 = "paste";
  51. typedef struct _list
  52. {
  53. struct _list *next;
  54. FILE *fp;
  55. int cnt;
  56. char *name;
  57. } LIST;
  58. static int
  59. parallel(char **argv)
  60. {
  61. LIST *lp;
  62. int cnt;
  63. wint_t ich;
  64. wchar_t ch;
  65. char *p;
  66. LIST *head, *tmp;
  67. int opencnt, output;
  68. for(cnt = 0, head = tmp = NULL; (p = *argv); ++argv, ++cnt)
  69. {
  70. if((lp = malloc(sizeof(LIST))) == NULL) utils_err(1, NULL);
  71. if(p[0] == '-' && !p[1])
  72. lp->fp = stdin;
  73. else if(!(lp->fp = fopen(p, "r")))
  74. utils_err(1, "%s", p);
  75. lp->next = NULL;
  76. lp->cnt = cnt;
  77. lp->name = p;
  78. if(!head)
  79. head = tmp = lp;
  80. else
  81. {
  82. tmp->next = lp;
  83. tmp = lp;
  84. }
  85. }
  86. for(opencnt = cnt; opencnt;)
  87. {
  88. for(output = 0, lp = head; lp; lp = lp->next)
  89. {
  90. if(!lp->fp)
  91. {
  92. if(output && lp->cnt && (ch = delim[(lp->cnt - 1) % delimcnt])) putwchar(ch);
  93. continue;
  94. }
  95. if((ich = getwc(lp->fp)) == WEOF)
  96. {
  97. if(!--opencnt) break;
  98. lp->fp = NULL;
  99. if(output && lp->cnt && (ch = delim[(lp->cnt - 1) % delimcnt])) putwchar(ch);
  100. continue;
  101. }
  102. /*
  103. * make sure that we don't print any delimiters
  104. * unless there's a non-empty file.
  105. */
  106. if(!output)
  107. {
  108. output = 1;
  109. for(cnt = 0; cnt < lp->cnt; ++cnt)
  110. if((ch = delim[cnt % delimcnt])) putwchar(ch);
  111. }
  112. else if((ch = delim[(lp->cnt - 1) % delimcnt]))
  113. putwchar(ch);
  114. if(ich == linedelim) continue;
  115. do
  116. {
  117. putwchar(ich);
  118. } while((ich = getwc(lp->fp)) != WEOF && ich != linedelim);
  119. }
  120. if(output) putwchar(linedelim);
  121. }
  122. return (0);
  123. }
  124. static int
  125. sequential(char **argv)
  126. {
  127. FILE *fp;
  128. int cnt, failed, needdelim;
  129. wint_t ch;
  130. char *p;
  131. failed = 0;
  132. for(; (p = *argv); ++argv)
  133. {
  134. if(p[0] == '-' && !p[1])
  135. fp = stdin;
  136. else if(!(fp = fopen(p, "r")))
  137. {
  138. utils_warn("%s", p);
  139. failed = 1;
  140. continue;
  141. }
  142. cnt = needdelim = 0;
  143. while((ch = getwc(fp)) != WEOF)
  144. {
  145. if(needdelim)
  146. {
  147. needdelim = 0;
  148. if(delim[cnt] != '\0') putwchar(delim[cnt]);
  149. if(++cnt == delimcnt) cnt = 0;
  150. }
  151. if(ch != linedelim)
  152. putwchar(ch);
  153. else
  154. needdelim = 1;
  155. }
  156. if(needdelim) putwchar(linedelim);
  157. if(fp != stdin) (void)fclose(fp);
  158. }
  159. return (failed != 0);
  160. }
  161. static int
  162. tr(wchar_t *arg)
  163. {
  164. int cnt;
  165. wchar_t ch, *p;
  166. for(p = arg, cnt = 0; (ch = *p++); ++arg, ++cnt)
  167. if(ch == '\\') switch(ch = *p++)
  168. {
  169. case 'n':
  170. *arg = '\n';
  171. break;
  172. case 't':
  173. *arg = '\t';
  174. break;
  175. case '0':
  176. *arg = '\0';
  177. break;
  178. default:
  179. *arg = ch;
  180. break;
  181. }
  182. else
  183. *arg = ch;
  184. if(!cnt) utils_errx(1, "no delimiters specified");
  185. return (cnt);
  186. }
  187. static void
  188. usage(void)
  189. {
  190. (void)fprintf(stderr, "usage: paste [-sz] [-d delimiters] [file...]\n");
  191. exit(1);
  192. }
  193. int
  194. main(int argc, char *argv[])
  195. {
  196. setlocale(LC_ALL, "");
  197. errno = 0;
  198. int c = -1, seq = 0;
  199. while((c = getopt(argc, argv, ":d:sz")) != -1)
  200. {
  201. switch(c)
  202. {
  203. case 'd':
  204. {
  205. const char *arg = optarg;
  206. size_t len = mbsrtowcs(NULL, &arg, 0, NULL);
  207. if(len == (size_t)-1) utils_err(1, "delimiters");
  208. if(len == SIZE_MAX) utils_err(1, NULL);
  209. wchar_t *warg = calloc((len + 1), sizeof(*warg));
  210. if(warg == NULL) utils_err(1, NULL);
  211. arg = optarg;
  212. len = mbsrtowcs(warg, &arg, len + 1, NULL);
  213. if(len == (size_t)-1) utils_err(1, "delimiters");
  214. delimcnt = tr(delim = warg);
  215. break;
  216. }
  217. case 's':
  218. seq = 1;
  219. break;
  220. case 'z':
  221. linedelim = L'\0';
  222. break;
  223. case ':':
  224. fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
  225. usage();
  226. return 1;
  227. case '?':
  228. fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
  229. usage();
  230. return 1;
  231. default:
  232. abort();
  233. }
  234. }
  235. argc -= optind;
  236. argv += optind;
  237. char *dash[] = {(char *)"-", NULL};
  238. char **args = argc > 0 ? argv : dash;
  239. if(!delim)
  240. {
  241. delimcnt = 1;
  242. delim = tab;
  243. }
  244. if(seq)
  245. return sequential(args);
  246. else
  247. return parallel(args);
  248. }