/* $Revision: 1.6 $ */ /* The implementation of the Scheme primitive `file-insertions'. */ #include #include #include #include #include "unroff.h" static int cmp(const void *p1, const void *p2) { struct S_String *s1, *s2; Object o1, o2; int tmp; o1 = *(Object *)p1, s1 = STRING(Car(o1)); o2 = *(Object *)p2, s2 = STRING(Car(o2)); if ((tmp = memcmp(s1->data, s2->data, s1->size < s2->size ? s1->size : s2->size)) == 0) { if (s1->size == s2->size) return Get_Integer(Car(Cdr(o1))) - Get_Integer(Car(Cdr(o2))); else return s1->size - s2->size; } return tmp; } #define Finish_File {\ if (fromfn) {\ copy_rest(from, to, fromfn, tofn);\ close(from);\ close(to);\ if (rename(tofn, fromfn) == -1) {\ Saved_Errno = errno;\ Primitive_Error("cannot rename ~s to ~s: ~E", tofn, fromfn);\ }\ free(tofn);\ }\ } static void copy(int from, int to, char *fromfn, char *tofn, int len, int off) { char buf[8192]; int n; while (len > 0) { if ((n = read(from, buf, len > 8192 ? 8192 : len)) == -1) read_error(fromfn); if (n == 0) fatal_error("file `%s' too short for insertion at offset %d", fromfn, off); if (write(to, buf, n) != n) write_error(tofn); len -= n; } } static void copy_rest(int from, int to, char *fromfn, char *tofn) { char buf[8192]; int n; while ((n = read(from, buf, 8192)) > 0) if (write(to, buf, n) != n) write_error(tofn); if (n == -1) read_error(fromfn); } static Object p_file_insertions(Object spec) { Object v, x; int i, t, from = 0, to = 0; /* make gcc -Wuninitialized happy */ char *currfn, *fromfn, *tofn = 0; /* ditto */ struct stat st; int off = 0, nextoff; /* ditto for `off' */ struct S_String *ins; v = P_List_To_Vector(spec); for (i = VECTOR(v)->size; --i >= 0; ) { x = VECTOR(v)->data[i]; Check_List(x); if (Fast_Length(x) < 3 || TYPE(Car(x)) != T_String || ((t = TYPE(Car(Cdr(x)))) != T_Fixnum && t != T_Bignum) || TYPE(Car(Cdr(Cdr(x)))) != T_String) Primitive_Error("invalid file insertion specification"); } qsort(VECTOR(v)->data, VECTOR(v)->size, sizeof(Object), cmp); for (fromfn = 0, i = 0; i < VECTOR(v)->size; i++) { x = VECTOR(v)->data[i]; currfn = Get_String(Car(x)); if (!fromfn || strcmp(fromfn, currfn) != 0) { Finish_File; fromfn = currfn; if ((from = open(fromfn, O_RDONLY)) == -1) open_error(fromfn); (void)fstat(from, &st); if (st.st_nlink > 1) { warn("links to `%s' exist; insertion skipped", fromfn); close(from); fromfn = 0; continue; } tofn = safe_malloc(strlen(fromfn) + 1 + 4); sprintf(tofn, "%s.new", fromfn); if ((to = open(tofn, O_WRONLY|O_CREAT|O_TRUNC, st.st_mode)) == -1) open_error(tofn); off = 0; } x = Cdr(x); nextoff = Get_Integer(Car(x)); if (nextoff < 0) Primitive_Error("invalid insertion offset ~a", Car(x)); x = Cdr(x); ins = STRING(Car(x)); copy(from, to, fromfn, tofn, nextoff-off, nextoff); if (write(to, ins->data, ins->size) != ins->size) write_error(tofn); off = nextoff; } Finish_File; return Void; } void init_insert(void) { Define_Primitive(p_file_insertions, "file-insertions", 1, 1, EVAL); }