apue, example 1
The first example in APUE shows the use of the C standard library to
implement a very simple ls. The C standard library abstracts away a
number of issues, namely :
- the system calls themselves are unbuffered, so the management of
providing a buffer and then parsing the directory entries from that
buffer is a complication abstracted by the
Cstandard library. - different operating systems implement the
Cstruct representing a directory entry. - the standard library in
Cabstracts this and optimises the manner in which directory entries are parsed for each operating system; similarly the Go authors have written a standard library for Go. The standard library in Go; although it has to use theCsystem calls; does not use theCstandard library.
Gotchas
- There must be no empty lines between the
Cpre-amble and theimport "C" - when converting a
Cstring to aGostring, you pass the address of the first element in the null terminated char array representing the string, such as&dirent.d_name[0]to theC.GoString(__)function.
Code Examples
As a result there are two examples in Go for this example:
- An example using
CGoto call theCstandard library to implement a very simplels.
package main
/*
#include <dirent.h>
#include <stdlib.h>
*/
import "C"
import (
"fmt"
"os"
"unsafe"
)
// dirent.h provides opendir, readdir, closedir
// stdlib.h provides free
// these calls are not sys calls, but standard library calls
// directory entries are represented different in different
// unix systems, and the system calls vary.
func main() {
if len(os.Args) != 2 {
fmt.Println("usage: myls directory_name")
os.Exit(1)
}
arg := C.CString(os.Args[1])
defer C.free(unsafe.Pointer(arg))
dir, err := C.opendir(arg)
if err != nil {
fmt.Printf("can't open %s: %v\n", os.Args[1], err)
os.Exit(1)
}
defer C.closedir(dir)
for dirent := C.readdir(dir); dirent != nil; dirent = C.readdir(dir) {
name := C.GoString(&dirent.d_name[0])
fmt.Println(name)
}
}
- An example using
Goto call theGostandard library to implement a very simplels.
package main
import (
"fmt"
"io/ioutil"
"os"
)
// dirent.h provides opendir, readdir, closedir
// stdlib.h provides free
// the equivalent standard library calls in
// golang are provide by io.ioutil
// the golang authors decided not to
// return the "." and ".." directory entries
func main() {
if len(os.Args) != 2 {
fmt.Println("usage: myls directory_name")
os.Exit(1)
}
dir, err := ioutil.ReadDir(os.Args[1])
if err != nil {
fmt.Printf("can't open %s: %v\n", os.Args[1], err)
os.Exit(1)
}
fmt.Println(".")
fmt.Println("..")
for _, f := range dir {
name := f.Name()
fmt.Println(name)
}
}
Functional Differences
-
The C standard library call’s directory entries are not returned in a determined manner. They are not ordered and may be returned in any order. The underlying unbuffered system call also does not return the entries in a determined or sorted order.
-
The Go standard library returns the directory entries in a sorted lexical order. As the underlying system call does not sort the entries, this sorting is done within the standard library after calling the underlying unbuffered system call.
-
The C standard library returns all directory entries. This includes the directory entries for
.and... The underlying system call also returns these entries. -
The Go standard library does not return
.and..in the list of directory entries. This means that the Go standard library removes these entries after calling the underlying system call, as the system call does return these entries.