logo

utils-std

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

paste.c (6918B)


  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 "../libutils/err.h"
  36. #include "../libutils/getopt_nolong.h"
  37. #include <assert.h>
  38. #include <errno.h>
  39. #include <limits.h>
  40. #include <locale.h>
  41. #include <stdint.h> // SIZE_MAX
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <string.h>
  45. #include <sys/types.h>
  46. #include <unistd.h>
  47. #include <wchar.h>
  48. static wchar_t *delim;
  49. static int delimcnt;
  50. static wint_t linedelim = L'\n';
  51. static wchar_t tab[] = L"\t";
  52. const char *argv0 = "paste";
  53. typedef struct _list
  54. {
  55. struct _list *next;
  56. FILE *fp;
  57. int cnt;
  58. char *name;
  59. } LIST;
  60. static int
  61. parallel(char **argv)
  62. {
  63. LIST *lp;
  64. int cnt;
  65. wint_t ich;
  66. wchar_t ch;
  67. char *p;
  68. LIST *head, *tmp;
  69. int opencnt, output;
  70. int err = 0;
  71. for(cnt = 0, head = tmp = NULL; (p = *argv); ++argv, ++cnt)
  72. {
  73. if((lp = malloc(sizeof(LIST))) == NULL) utils_err(1, NULL);
  74. if(p[0] == '-' && !p[1])
  75. lp->fp = stdin;
  76. else
  77. {
  78. lp->fp = fopen(p, "r");
  79. if(!lp->fp) utils_err(1, "%s", p);
  80. assert(lp->fp != stdin);
  81. }
  82. lp->next = NULL;
  83. lp->cnt = cnt;
  84. lp->name = p;
  85. if(!head)
  86. head = tmp = lp;
  87. else
  88. {
  89. tmp->next = lp;
  90. tmp = lp;
  91. }
  92. }
  93. for(opencnt = cnt; opencnt;)
  94. {
  95. for(output = 0, lp = head; lp; lp = lp->next)
  96. {
  97. if(!lp->fp)
  98. {
  99. if(output && lp->cnt && (ch = delim[(lp->cnt - 1) % delimcnt])) putwchar(ch);
  100. continue;
  101. }
  102. if((ich = getwc(lp->fp)) == WEOF)
  103. {
  104. if(!--opencnt) break;
  105. if(lp->fp && lp->fp != stdin)
  106. {
  107. if(fclose(lp->fp) != 0)
  108. {
  109. fprintf(stderr,
  110. "%s: error: Failed closing file '%s': %s\n",
  111. argv0,
  112. lp->name,
  113. strerror(errno));
  114. err = 1;
  115. }
  116. }
  117. lp->fp = NULL;
  118. if(output && lp->cnt && (ch = delim[(lp->cnt - 1) % delimcnt])) putwchar(ch);
  119. continue;
  120. }
  121. /*
  122. * make sure that we don't print any delimiters
  123. * unless there's a non-empty file.
  124. */
  125. if(!output)
  126. {
  127. output = 1;
  128. for(cnt = 0; cnt < lp->cnt; ++cnt)
  129. if((ch = delim[cnt % delimcnt])) putwchar(ch);
  130. }
  131. else if((ch = delim[(lp->cnt - 1) % delimcnt]))
  132. putwchar(ch);
  133. if(ich == linedelim) continue;
  134. do
  135. {
  136. putwchar(ich);
  137. } while((ich = getwc(lp->fp)) != WEOF && ich != linedelim);
  138. }
  139. if(output) putwchar(linedelim);
  140. }
  141. for(lp = head; lp;)
  142. {
  143. if(lp->fp && lp->fp != stdin)
  144. {
  145. if(fclose(lp->fp) != 0)
  146. {
  147. fprintf(
  148. stderr, "%s: error: Failed closing file '%s': %s\n", argv0, lp->name, strerror(errno));
  149. err = 1;
  150. }
  151. }
  152. LIST *next = lp->next;
  153. free(lp);
  154. lp = next;
  155. }
  156. return err;
  157. }
  158. static int
  159. sequential(char **argv)
  160. {
  161. FILE *fp;
  162. int cnt, failed, needdelim;
  163. wint_t ch;
  164. char *p;
  165. failed = 0;
  166. for(; (p = *argv); ++argv)
  167. {
  168. if(p[0] == '-' && !p[1])
  169. fp = stdin;
  170. else if(!(fp = fopen(p, "r")))
  171. {
  172. utils_warn("%s", p);
  173. failed = 1;
  174. continue;
  175. }
  176. cnt = needdelim = 0;
  177. while((ch = getwc(fp)) != WEOF)
  178. {
  179. if(needdelim)
  180. {
  181. needdelim = 0;
  182. if(delim[cnt] != '\0') putwchar(delim[cnt]);
  183. if(++cnt == delimcnt) cnt = 0;
  184. }
  185. if(ch != linedelim)
  186. putwchar(ch);
  187. else
  188. needdelim = 1;
  189. }
  190. if(needdelim) putwchar(linedelim);
  191. if(fp != stdin)
  192. {
  193. if(fclose(fp) != 0)
  194. {
  195. fprintf(stderr, "%s: error: Failed closing file '%s': %s\n", argv0, p, strerror(errno));
  196. failed = 1;
  197. }
  198. }
  199. }
  200. return (failed != 0);
  201. }
  202. static int
  203. tr(wchar_t *arg)
  204. {
  205. int cnt;
  206. wchar_t ch, *p;
  207. for(p = arg, cnt = 0; (ch = *p++); ++arg, ++cnt)
  208. if(ch == '\\') switch(ch = *p++)
  209. {
  210. case 'n':
  211. *arg = '\n';
  212. break;
  213. case 't':
  214. *arg = '\t';
  215. break;
  216. case '0':
  217. *arg = '\0';
  218. break;
  219. default:
  220. *arg = ch;
  221. break;
  222. }
  223. else
  224. *arg = ch;
  225. if(!cnt) utils_errx(1, "no delimiters specified");
  226. return (cnt);
  227. }
  228. static void
  229. usage(void)
  230. {
  231. (void)fprintf(stderr, "usage: paste [-sz] [-d delimiters] [file...]\n");
  232. exit(1);
  233. }
  234. int
  235. main(int argc, char *argv[])
  236. {
  237. char *lc_all = setlocale(LC_ALL, "");
  238. if(lc_all == NULL)
  239. {
  240. fprintf(stderr,
  241. "%s: warning: Failed loading locales. setlocale(LC_ALL, \"\"): %s\n",
  242. argv0,
  243. strerror(errno));
  244. }
  245. errno = 0;
  246. int seq = 0;
  247. for(int c = -1; (c = getopt_nolong(argc, argv, ":d:sz")) != -1;)
  248. {
  249. switch(c)
  250. {
  251. case 'd':
  252. {
  253. const char *arg = optarg;
  254. size_t len = mbsrtowcs(NULL, &arg, 0, NULL);
  255. if(len == (size_t)-1) utils_err(1, "delimiters");
  256. if(len == SIZE_MAX) utils_err(1, NULL);
  257. wchar_t *warg = calloc((len + 1), sizeof(*warg));
  258. if(warg == NULL) utils_err(1, NULL);
  259. arg = optarg;
  260. len = mbsrtowcs(warg, &arg, len + 1, NULL);
  261. if(len == (size_t)-1) utils_err(1, "delimiters");
  262. delimcnt = tr(delim = warg);
  263. break;
  264. }
  265. case 's':
  266. seq = 1;
  267. break;
  268. case 'z':
  269. linedelim = L'\0';
  270. break;
  271. case ':':
  272. fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
  273. usage();
  274. return 1;
  275. case '?':
  276. GETOPT_UNKNOWN_OPT
  277. usage();
  278. return 1;
  279. default:
  280. abort();
  281. }
  282. }
  283. argc -= optind;
  284. argv += optind;
  285. char *dash[] = {(char *)"-", NULL};
  286. char **args = argc > 0 ? argv : dash;
  287. if(!delim)
  288. {
  289. delimcnt = 1;
  290. delim = tab;
  291. }
  292. if(seq)
  293. return sequential(args);
  294. else
  295. return parallel(args);
  296. }